SP30922 题解
这是一个静态的树上路径统计问题,可以使用树上差分解决。
树上任意两个点 \((x,y)\) 之间的路径是唯一的,所以我们考虑两个端点对这条路径的影响。我们可以把这条路径拆成两段,一段是 \((x,LCA_{x,y})\) ,另一段是 \((LCA_{x,y},y)\) ,对这两条路径做修改。
我们设每个节点 \(i\) 有点权 \(f_i\) ,其子树的点权和为 \(s_i\) ,对于 \((x,y)\) 的修改,我们直接给 \(f_x\) 和 \(f_y\) 增加 \(1\) ,给 \(f_{LCA_{x,y}}\) 和 $ f_{fa_{LCA_{x,y}}}$ 减去 \(1\) ,它的意义就是:
- 把从 \(x\) 和从 \(y\) 到根节点的路径的 \(s_i\) 增加
然而又由于,从 \(LCA_{x,y}\) 开始,两条路径会并到一起,而从 \(fa_{LCA_{x,y}}\) 开始,又不需要路径修改,所以:
- 给 \(LCA_{x,y}\) 和 \(fa_{LCA_{x,y}}\) 到根节点的路径的 \(s_i\) 减小
最终使用 dfs 求出 \(s_i\) ,即为答案。
使用了树剖求 LCA ,效率较倍增 LCA 快了不少,未使用快读快写和卡常也到了最优解第五。理论上可以使用 Tarjan \(O(n)\) 求出 LCA ,但是我太弱了不会写,感兴趣的可以写一下然后拿到最优解榜一 /bx/bx/bx
Code
#include <bits/stdc++.h>
using namespace std;
const int N=4e5+10;
int n;
struct edge{
int v,nxt;
}e[N*2];
int head[N],cnt=1;
void add(int u,int v){
e[cnt].v=v;
e[cnt].nxt=head[u];
head[u]=cnt++;
}
int dep[N],fa[N],son[N],siz[N];
void dfs1(int u,int fat){
fa[u]=fat;
dep[u]=dep[fat]+1;
siz[u]=1;
for(int i=head[u];i;i=e[i].nxt){
if(e[i].v!=fat){
dfs1(e[i].v,u);
siz[u]+=siz[e[i].v];
if(siz[son[u]]<siz[e[i].v])
son[u]=e[i].v;
}
}
}
int top[N];
void dfs2(int u,int topf){
top[u]=topf;
if(son[u])dfs2(son[u],topf);
for(int i=head[u];i;i=e[i].nxt){
if(!top[e[i].v])
dfs2(e[i].v,e[i].v);
}
}
int LCA(int x,int y){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
x=fa[top[x]];
}
if(dep[x]>dep[y])swap(x,y);
return x;
}
int f[N],s[N];
void dfs(int u,int fa){
s[u]=f[u];
for(int i=head[u];i;i=e[i].nxt){
if(e[i].v!=fa){
dfs(e[i].v,u);
s[u]+=s[e[i].v];
}
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1;i<n;i++){
int u,v;
cin>>u>>v;
add(u,v);add(v,u);
}
dfs1(1,0);
dfs2(1,1);
for(int i=2;i<=n;i++){
f[i-1]++;
f[i]++;
int lca=LCA(i,i-1);
f[lca]--;
f[fa[lca]]--;
}
dfs(1,0);
for(int i=1;i<=n;i++)cout<<s[i]<<'\n';
return 0;
}