长链剖分初步

长链剖分和重链剖分的区别在于每个点的特殊儿子判断的方式从子树大小变成了最深深度大小。
这样的话,我们在维护某些信息的时候可以直接继承其特殊儿子的信息,就能够优化复杂度。

优化dp的例题
首先这个题有一个n^2的dp f [ u ] [ j + 1 ] = f [ v ] [ j ] f[u][j+1]=\sum f[v][j] 表示第u个节点,深度为j的点的数量。
然后考虑用长链剖分优化,对于每一个节点,直接继承特殊儿子的dp数组,然后递归计算其它儿子的贡献,这个复杂度是 O ( n ) O(n) 的。

考虑证明:每条链只会被合并一次

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
inline int read(){
	char c=getchar();int t=0,f=1;
	while((!isdigit(c))&&(c!=EOF)){if(c=='-')f=-1;c=getchar();}
	while((isdigit(c))&&(c!=EOF)){t=(t<<3)+(t<<1)+(c^48);c=getchar();}
	return t*f;
}
int n;
struct edge{
	int v,p;
}e[maxn<<1];
int h[maxn],cnt;
void add(int a,int b){
	e[++cnt].p=h[a];
	e[cnt].v=b;
	h[a]=cnt;
	e[++cnt].p=h[b];
	e[cnt].v=a;
	h[b]=cnt;
}
int dep[maxn],alfa[maxn],son[maxn],f[maxn];
void dfs1(int u,int fa){
	f[u]=fa;
	dep[u]=dep[fa]+1;son[u]=0;alfa[u]=dep[u];
	for(int i=h[u];i;i=e[i].p){
		int v=e[i].v;
		if(v==fa)continue;
		dfs1(v,u);
		if(alfa[v]>alfa[son[u]])son[u]=v;
		alfa[u]=max(alfa[u],alfa[v]);
	}
	//printf("%d %d\n",son[u],u);
}
int ans[maxn],*dp[maxn],tmp[maxn<<5],*now=tmp+1;//这里必须要用指针数组,不然空间不行。
inline void start(int x){
	dp[x]=now;now+=alfa[x]-dep[x]+1;
}
void dfs2(int u,int fa){
	if(son[u]){dp[son[u]]=dp[u]+1,dfs2(son[u],u);ans[u]=ans[son[u]]+1;}//由于是指针数组,继承儿子的答案的时候就比较方便,只需要把地址-1就可以了。
	dp[u][0]=1;
	for(int i=h[u];i;i=e[i].p){
		int v=e[i].v;
		if(v==fa||v==son[u])continue;
		start(v);
		dfs2(v,u);
		for(int j=0;j<=alfa[v]-dep[v];j++){
			dp[u][j+1]+=dp[v][j];
			if(dp[u][j+1]>dp[u][ans[u]]||(dp[u][j+1]==dp[u][ans[u]]&&j+1<ans[u]))ans[u]=j+1;
		}
	}
	if(dp[u][ans[u]]==1)ans[u]=0;
}
signed main(){
	n=read();
	for(int i=1;i<n;i++){
		int a=read(),b=read();
		add(a,b);
	}
	dfs1(1,0);
	start(1);
	dfs2(1,0);
	for(int i=1;i<=n;i++)
	printf("%d\n",ans[i]);
	return 0;
}

发布了95 篇原创文章 · 获赞 9 · 访问量 3175

猜你喜欢

转载自blog.csdn.net/wmhtxdy/article/details/104825885