当前位置: 首页 > article >正文

【lca,树上差分】P3128 [USACO15DEC] Max Flow P

题意

给定大小为 n ( 2 ≤ n ≤ 5 × 1 0 4 ) n(2 \leq n \leq 5 \times 10^4) n(2n5×104) 的树,并给定 m ( 1 ≤ m ≤ 1 0 5 ) m(1 \leq m \leq 10^5) m(1m105) 条树上的路径(给定两个端点,容易证明两个点树上路径唯一),求上述路径中最多有几条路径同时经过一个点。

【样例输入】

5 10
3 4
1 5
4 2
5 4
5 4
5 4
3 5
4 3
4 3
1 3
3 5
5 4
1 5
3 4

【样例输出】
9
在这里插入图片描述

思路

在这里插入图片描述

考虑树上差分。树上差分,顾名思义,就是在树上进行类似差分的操作。

假设有一条 6 ← 4 6 \gets 4 64 的路径。

如果要暴力标记,则单次复杂度最坏情况下近似于 O ( n ) O(n) O(n),时间爆炸。

但是,如果我们把原路径看作 6 ← 1 + 1 ← 5 6 \gets 1 + 1 \gets 5 61+15 两条路径的和的话,我们就会有一些发现:

对于(更改后)路径上每一个点,我们只需要标记头尾,就可以在树上形成类似差分的东西。

考虑如何差分,记差分数组为 c h a f e n chafen chafen c h a f e n i chafen_i chafeni 表示第 i i i 个点及其所有祖先的路径数量之和中经过节点 i i i 的那一部分,要标记一条由 i i i j j j 的路径。

c h a f e n x ← c h a f e n x + 1 , c h a f e n y ← c h a f e n y + 1 , c h a f e n l c a ( x , y ) ← c h a f e n l c a ( x , y ) − 1 , c h a f e n f a l c a ( x , y ) ← c h a f e n f a l c a ( x , y ) − 1 chafen_x \gets chafen_x + 1,chafen_y \gets chafen_y + 1,chafen_{lca(x,y)} \gets chafen_{lca(x,y)} - 1,chafen_{fa_{lca(x,y)}} \gets chafen_{fa_{lca(x,y)}} - 1 chafenxchafenx+1,chafenychafeny+1,chafenlca(x,y)chafenlca(x,y)1,chafenfalca(x,y)chafenfalca(x,y)1

拿上图中 6 ← 4 6 \gets 4 64 举例,可以发现 l c a ( 4 , 6 ) = 1 lca(4,6) = 1 lca(4,6)=1

我们标记 c h a f e n 6 , c h a f e n 4 chafen_6,chafen_4 chafen6,chafen4 加上 1 1 1,可以发现 c h a f e n 1 chafen_1 chafen1 作为两者共同祖先,加了两次,故要扣掉。

因为两个的路径不包括他们最近共同祖先的祖先,所以 c h a f e n f a l c a ( x , y ) chafen_{fa_{lca(x,y)}} chafenfalca(x,y) 也要减一。

代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m;
int head[50005],nex[100005],to[100005],cnt = 0;
int deep[50005],max_deep;
int d[50005][25];

int chafen[50005];
void add(int x,int y) {
	nex[++cnt] = head[x];
	head[x] = cnt;
	to[cnt] = y;
}
void find_deep(int now,int fa) {
	if(now != fa)d[now][0] = fa;
	for(int i = head[now];i;i = nex[i]) {
		if(to[i] != fa) {
			deep[to[i]] = deep[now] + 1;
			find_deep(to[i],now); 
		}
	}
	max_deep = max(max_deep,deep[now]);
	return;
}
void jump1(int &x,int &y) {
	int del_deep = deep[x] - deep[y];
	int r = 0;
	while(del_deep) {
		if(del_deep & 1) {
			x = d[x][r];
		}
		r++;
		del_deep >>= 1;
	}
	return;
}
int jump(int x,int y) {
	int r = 0;
	while((1 << r) <= deep[x]) r++;
	r--;
	for(;r >= 0;r--) {
		if(d[x][r] != d[y][r]) {
			x = d[x][r];
			y = d[y][r];
		}
	}
	if(x == y) return x;
	else return d[x][0];
}
int ans = -1e18;
int lca(int x,int y) {
	if(deep[y] > deep[x]) swap(x,y);
	if(deep[x] != deep[y]) jump1(x,y);
	if(x == y) return x;
	return jump(x,y);
}
void dfs(int now,int fa) {
	for(int i = head[now];i;i = nex[i]) {
		if(to[i] != fa) dfs(to[i],now),chafen[now] += chafen[to[i]];
	}
	ans = max(ans,chafen[now]);
	//printf("%lld %lld\n",now,chafen[now]);
	return;
}
signed main() {
	scanf("%lld %lld",&n,&m);
	for(int i = 1;i < n;i++) {
		int x,y;
		scanf("%lld %lld",&x,&y);
		add(x,y),add(y,x);
	} 
	find_deep(1,1);
	for(int i = 1;(1 << i) <= max_deep;i++) {
		for(int j = 1;j <= n;j++) {
			if(deep[j] < (1 << i)) continue;
			d[j][i] = d[d[j][i - 1]][i - 1];
		}
	}
	for(int i = 1;i <= m;i++) {
		int x,y;
		scanf("%lld %lld",&x,&y);
		int z = lca(x,y);
		chafen[x]++,chafen[y]++,chafen[z]--,chafen[d[z][0]]--;
	}
	dfs(1,1);
	printf("%lld\n",ans);
	return 0;
}

AC记录


http://www.kler.cn/news/363752.html

相关文章:

  • ESP32移植Openharmony外设篇(3)OLED屏
  • 【摄像头产品介绍性能特点优点】
  • 外包干了2个月,技术明显退步
  • 顺序表(一)(数据结构)
  • 【部署篇】RabbitMq-03集群模式部署
  • GRU神经网络理解
  • 显示指定目录下的 .c 文件 Linux环境 C语言实现
  • 解释 RESTful API,以及如何使用它构建 web 应用程序。
  • 0 Day漏洞利用激增:谷歌Mandiant警示新安全趋势
  • 【springboot应用-RestTemplate】
  • RHCE--nginx实现多IP访问多网站
  • 形式架构定义语言(ADL)
  • React综合指南(二)
  • Threejs 实现3D 地图(02)创建3d 地图
  • 【python】sorted() list.sort()
  • LeetCode300:最长递增子序列
  • 【网络安全】简单P1:通过开发者工具解锁专业版和企业版功能
  • PostgreSQL DBA月度检查列表
  • 05 go语言(golang) - 常量和条件语句
  • C++(标准输入输出流、命名空间、string字符串、引用)
  • 怎么快速在ppt中添加文本框?2个常用的ppt使用技巧盘点!
  • 【Linux实验】拆分文件命令
  • 【zookeeper】集群配置
  • MySQL的 Next-Key Lock 底层原理详解
  • Leetcode 赎金信
  • Matlab|基于氢储能的热电联供型微电网优化调度方法