树的重心

树的重心

性质:一个点是重心,等价于,以这个点为根,它的每个子树的大小,都不会超过整个树大小的一半(充要条件)

性质及其证明

POJ3107

模板

这题卡vector

注意判断数组越界

void dfs(int i,int fa){
	siz[i]=1;
	int tmp=0;
	for(int j=head[i];~j;j=e[j].next){
		int v=e[j].to;
		if(v!=fa){
			dfs(v,i);
			siz[i]+=siz[v];
			tmp=max(tmp,siz[v]);
		}
	}
	tmp=max(tmp,n-siz[i]);
	if(tmp<minn){
		minn=tmp;
		pos=1;
		ans[pos]=i;
	}else if(tmp==minn){
		ans[++pos]=i;
	}
}

CF359(div1)B Kay and Snowflake

给定一棵树,根为\(1\),找到所有子树的重心

性质

设当前子树的根为\(u\),子节点为\(v_i\)\(hc(u)\)表示以\(u\)为根的子树的重心

\(hc(u)\)在路径\((u,hc(v_i))\)

即当前子树的重心,在当前子树的子树的重心到当前子树的根的路径上

通过这个性质,这道题首先递归初始化数组,然后自下而上地,让子节点的重心向上跳,来得到父节点的重心

void dfs(int i,int fa){
	siz[i]=1;
	ans[i]=i;
	for(int j=0;j<(int)a[i].size();j++){
		int v=a[i][j];
		if(v!=fa){
			dfs(v,i);
			siz[i]+=siz[v];
			maxn[i]=max(maxn[i],siz[v]);
		}
	}
	for(int j=0;j<(int)a[i].size();j++){
		int v=a[i][j];
		if(v!=fa){
			int k=ans[v];
			while(k!=i){
				if(max(maxn[k],siz[i]-siz[k])<=siz[i]/2){
					ans[i]=k;
					break;
				}else k=f[k];
			}
		}	
	}
}

给定一棵节点数为\(n\)的树,删一条边然后加上一条边,使得该树的重心唯一(删掉的边和加上的边可以是同一条)

如果这棵树只有一个重心,就任选一边去删,然后再加回来

如果这棵树有两个重心,那么有这样的性质

这两个重心一定相邻,删掉这两点之间的边,获得的两棵子树大小相等

所以只需让这两棵树大小不相等,从其中一棵树删边,再加到另一棵树上即可

P5666

给定一棵树,求单独删除每一条边后分裂出的两棵子树的重心编号之和

换根+倍增

性质

设当前子树的根为\(u\),重子节点为\(v\)\(hc(u)\)表示以\(u\)为根的子树的重心

\(x\)不是\(hc(u)\),则\(hc(u)\)在以\(v\)为根的子树上

即如果根不是当前子树的重心,那么当前子树的重心在重子子树上

于是定义\(hs[u][k]\)表示\(u\)节点向重子方向跳\(2^k\)步所到达的节点

\(hs[u][0]\)即为\(u\)节点的重子

对于每一条边\((u,v)\),不妨设\(u\)为父节点,\(v\)为子节点

分别处理以\(u\)为根和以\(v\)为根的两棵子树

对于\(v\)子树

直接向下倍增找到重心即可

判断重心利用性质一个点是重心,等价于,以这个点为根,它的每个子树的大小,都不会超过整个树大小的一半(充要条件)

对于\(u\)子树

首先维护\(siz[u]\)

然后考虑到\(v\)节点可能是\(u\)节点的重子,而此时两棵树已经分裂,所以要额外维护各个节点的次重子

定义\(shs[u]\)表示\(u\)节点的次重子

所以当\(v==hs[u][0]\)时,令\(hs[u][0]=shs[u]\)

另外,当前\(u\)节点的重子还有可能是\(u\)节点的父节点

定义\(f[u]\)表示\(u\)节点的父节点

如果\(siz[f[u]]>siz[hs[u][0]]\),令\(hs[u][0]=f[u]\)

由于重子的改变,所以更新\(hs\)数组

最后向下倍增找到重心即可

分别处理完以\(u\)为根和以\(v\)为根的两棵子树后

维护\(f[u]=v\),然后递归

在处理完所有\(u\)节点的子节点\(v\)后,恢复\(hs[u][0],siz[u],f[u]\)\(hs\)数组

ps:对于存在两个重心的情况,判断倍增得到的重心的父节点是否为重心

void init(int i,int fa){
	siz[i]=1;
	for(int j=head[i];~j;j=e[j].next){
		int v=e[j].v;
		if(v==fa)continue;
		f[v]=i;
		init(v,i);
		siz[i]+=siz[v];
		if(siz[v]>siz[hs[i][0]]){
			shs[i]=hs[i][0];
			hs[i][0]=v;
		}else if(siz[v]>siz[shs[i]]){
			shs[i]=v;
		}
	}
	for(int k=1;k<=20;k++){
		hs[i][k]=hs[hs[i][k-1]][k-1];
	}
}
bool check(int i,int s){
	return max(s-siz[i],siz[hs[i][0]])<=(s/2);
}
void update(int i){
	for(int k=1;k<=20;k++){
		hs[i][k]=hs[hs[i][k-1]][k-1];
	}
}
void dfs(int i,int fa){
	int prehs=hs[i][0],presiz=siz[i];
	for(int j=head[i];~j;j=e[j].next){
		int v=e[j].v;
		if(v==fa)continue;
		
		if(v==prehs)hs[i][0]=shs[i];
		else hs[i][0]=prehs;
		if(siz[fa]>siz[hs[i][0]])hs[i][0]=fa;
		
		update(i);
		
		siz[i]=n-siz[v],f[i]=0,f[v]=0;
		
		int p=i;
		for(int k=20;~k;k--)
			if(siz[i]-siz[hs[p][k]]<=(siz[i]/2))
				p=hs[p][k];
		ans+=p+f[p]*check(f[p],siz[i]);
		
		p=v;
		for(int k=20;~k;k--)
			if(siz[v]-siz[hs[p][k]]<=(siz[v]/2))
				p=hs[p][k];
		ans+=p+f[p]*check(f[p],siz[v]);
		
		f[i]=v;
		
		dfs(v,i);
	}
	hs[i][0]=prehs,siz[i]=presiz,f[i]=fa,update(i);
}
posted @ 2024-08-20 21:15  wertyuio1  阅读(78)  评论(0)    收藏  举报