题意
给定大小为 n ( 2 ≤ n ≤ 5 × 1 0 4 ) n(2 \leq n \leq 5 \times 10^4) n(2≤n≤5×104) 的树,并给定 m ( 1 ≤ m ≤ 1 0 5 ) m(1 \leq m \leq 10^5) m(1≤m≤105) 条树上的路径(给定两个端点,容易证明两个点树上路径唯一),求上述路径中最多有几条路径同时经过一个点。
【样例输入】
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 6←4 的路径。
如果要暴力标记,则单次复杂度最坏情况下近似于 O ( n ) O(n) O(n),时间爆炸。
但是,如果我们把原路径看作 6 ← 1 + 1 ← 5 6 \gets 1 + 1 \gets 5 6←1+1←5 两条路径的和的话,我们就会有一些发现:
对于(更改后)路径上每一个点,我们只需要标记头尾,就可以在树上形成类似差分的东西。
考虑如何差分,记差分数组为 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 chafenx←chafenx+1,chafeny←chafeny+1,chafenlca(x,y)←chafenlca(x,y)−1,chafenfalca(x,y)←chafenfalca(x,y)−1
拿上图中 6 ← 4 6 \gets 4 6←4 举例,可以发现 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记录