树的重心

一、树的重心的定义

对于一棵树,设以\(A\)为根,若\(A\)的子树节点数最大值,在所有节点中最小,则\(A\)为树的重心。

二、树的重心的性质

它有诸多性质如下:

  1. 一棵树最多有两个重心,且相邻。

  2. 当以树的重心为根时,设节点\(i\)的子树大小为\(siz_i\),根节点为\(r\),则\(\forall i\in children(r),\quad siz_i\leq \lfloor siz_r/2\rfloor\)。反过来若\(\forall i\in children(r),\quad siz_i\leq \lfloor siz_r/2\rfloor\),则\(r\)为重心。

  3. \(a\)\(b\)距离为\(d(a,b)\),点集为\(V\),若当\(c\)使\(\sum_{v\in V} d(v,c)\)最小,则\(c\)为重心。若有两个重心\(c_1\)\(c_2\)\(\sum_{v\in V} d(v,c_1)=\sum_{v\in V} d(v,c_2)\)相等。

  4. 若将两树\(A\)\(B\),以一边相接得到一棵新树\(C\),则\(C\)的重心在\(A\)的重心与\(B\)的重心的路径上。

  5. 若在一棵树上添加或删除一个叶子节点,那么它的重心最多只移动一条边的距离。

  6. 一棵树的重心一定在根节点所在的重链上。一棵树的重心一定是以该树根节点重儿子为根的子树的重心的祖先。

三、求重心及代码方面

dfs一遍,处理出每一个点子树大小与重儿子(若有多个可选,任选一个),与此同时计算每一个子树的重心。

此时因为性质6,父节点的重心(设为\(c_1\))为重儿子的重心(设为\(c_2\))祖先,所以在dfs中求得\(c_2\),从\(c_2\)处从下往上跳,当满足性质2的判断条件即为\(c_1\)

注: 关于性质2判断,可以通过事先处理的每一个子树的大小来判断。

模板例题:传送CodeForces

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define N 300005 
ll fa[N];
ll n,q;
vector<ll> son[N];
ll w[N]; 
ll t[N];
ll m[N];//重儿子 
bool check(ll v,ll u){
	return t[m[v]]<=t[u]>>1&&t[u]+1>>1<=t[v];
}//v是以u为根节点子树的重心吗? 
void dfs(ll u){
	t[u]=1;w[u]=u;
	for(int i=0;i<son[u].size();i++){
		dfs(son[u][i]);
		t[u]+=t[son[u][i]]; 
		if(t[son[u][i]]>t[m[u]])m[u]=son[u][i];
	}
	if(son[u].size()!=0){
		ll tmp=w[m[u]];
		while(!check(tmp,u)){
			tmp=fa[tmp];
		}
		w[u]=tmp;
	}	
}
int main(){
	cin>>n>>q;
	for(int i=2;i<=n;i++){
		cin>>fa[i];
		son[fa[i]].push_back(i);
	}
	dfs(1);
	while(q--){
		ll u;
		cin>>u;
		cout<<w[u]<<'\n';
	}
} 
posted @ 2025-07-21 22:44  Shawn2012  阅读(42)  评论(0)    收藏  举报