树的重心
定义
如果在树中选择某个节点并删除,这棵树将分为若干棵子树,统计子树节点数并记录最大值。取遍树上所有节点,使此最大值取到最小的节点被称为整个树的重心。、
性质
树的重心如果不唯一,则至多有两个,且这两个重心相邻。
以树的重心为根时,所有子树的大小都不超过整棵树大小的一半。
树中所有点到某个点的距离和中,到重心的距离和是最小的;如果有两个重心,那么到它们的距离和一样。
把两棵树通过一条边相连得到一棵新的树,那么新的树的重心在连接原来两棵树的重心的路径上。
在一棵树上添加或删除一个叶子,那么它的重心最多只移动一条边的距离。
当某节点 u 的最大子树节点 v 的 size 的两倍小于 u的size,那么v就是这个子树的重心
求法
首先先计算子树的大小,用dfs求出所有子树的大小,以当前节点为根分为向上向下两棵树,向下的节点数为size V 那么向上的节点数为n-size V;
然后考虑节点x和其每个儿子节点y,若x子树的重心在y子树内,则x子树的重心一定在y子树的重心的上方,所以我们从y子树的重心往上走,找到更优的就更新,否则,再往上跳显然答案会更加劣,所以结束即可。
因为这样更新每次结束都是这个子树的重心,下次循环又是这个子树的开始,所以一共经过每个点一次,所以复杂度可以优化为o(n);
void dfs(i64 now ) {
siz [ now ] = 1;//树的节点数
ans [ now ] = now;//重心位置初始化为自己
for (auto it : t [ now ]) {
dfs(it);
siz [ now ] += siz [ it ];
weight [ now ] = max(weight [ now ] , siz [ it ]);//找到最大的子树节点数
}
for (auto it : t [ now ]) {
i64 p = ans [ it ];//从儿子的重心开始向上遍历
while (p != now) {
if (max(weight[p], siz[now] - siz[p]) <= siz[now] / 2)//性质当某节点 u 的最大子树节点 v 的 size 的两倍小于 u的size,那么v就是这个子树的重心
{
ans[now] = p;//更新结果
break;
} else
p = fa[p];//向上找
}
}
}
模板题
https://codeforces.com/problemset/problem/685/B
题意
输入一棵树,判断每一棵子树的重心是哪一个节点.
输入
第一行两个整数 n,m代表节点数量和查询次数
第二行n个整数,分别代表第(2~n)个节点的父亲是哪个节点
接下来m行每行一个整数ai,代表查询以ai为根的子树重心是哪一个节点
AC代码
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
using i64 = long long;
i64 siz [ 300005 ],weight[300005],ans[300005];//siz树的大小,weirht最大子树,ans重心位置
i64 fa [ 300005 ];//节点i的父节点
vector <i64> t [ 300005 ];//存储所有子节点
i64 n , m;
void dfs(i64 now ) {
siz [ now ] = 1;//树的大小
ans [ now ] = now;//重心位置初始化为自己
for (auto it : t [ now ]) {
dfs(it);
siz [ now ] += siz [ it ];
weight [ now ] = max(weight [ now ] , siz [ it ]);//找到最大的子树节点数
}
for (auto it : t [ now ]) {
i64 p = ans [ it ];//从儿子的重心开始向上遍历
while (p != now) {
if (max(weight[p], siz[now] - siz[p]) <= siz[now] / 2)//性质当某节点 u 的最大子树节点 v 的 size 的两倍小于 u的size,那么v就是这个子树的重心
{
ans[now] = p;//更新结果
break;
} else
p = fa[p];//向上找
}
}
}
int main() {
cin >> n >> m;
for (int i = 2; i <=n; i++) {
i64 x;
cin >> x;
fa [ i ] = x;
t [ x ].push_back(i);
}
dfs(1 );
while (m--) {
i64 x;
cin >> x;
cout << ans [ x ] << endl;
}
return 0;
}

浙公网安备 33010602011771号