树的重心
一、树的重心的定义
对于一棵树,设以\(A\)为根,若\(A\)的子树节点数最大值,在所有节点中最小,则\(A\)为树的重心。
二、树的重心的性质
它有诸多性质如下:
-
一棵树最多有两个重心,且相邻。
-
当以树的重心为根时,设节点\(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\)为重心。
-
设\(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)\)相等。
-
若将两树\(A\)、\(B\),以一边相接得到一棵新树\(C\),则\(C\)的重心在\(A\)的重心与\(B\)的重心的路径上。
-
若在一棵树上添加或删除一个叶子节点,那么它的重心最多只移动一条边的距离。
-
一棵树的重心一定在根节点所在的重链上。一棵树的重心一定是以该树根节点重儿子为根的子树的重心的祖先。
三、求重心及代码方面
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';
}
}

浙公网安备 33010602011771号