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;
}
posted @ 2024-01-01 09:15  Linge_Zzzz  阅读(3)  评论(0编辑  收藏  举报  来源