【笔记】 树的重心
定义
对于一棵无根树,找到一个点,使得该树变成以该点为根的有根树时,最大子树的节点数最小,即删除该点后最大联通块的节点数最小。那么该点就是树的重心。
性质
- 树的重心可能不唯一,但一棵树最多有两个重心,且这两个重心相邻。
- 以树的重心为根时,所有子树的大小都不会超过整棵树的一半。
- 树中所有点到某个点的距离和中,到重心的距离和是最小的;如果有两个重心,那么到它们的距离和一样。
- 把两棵树通过一条边相连得到一棵新的树,那么新的树的重心在连接原来两棵树的重心的路径上。
- 在一棵树上添加或删除一个叶子,那么其重心最多只移动一条边的距离。
求法
思路
用一次搜索,在将无根树转为有根树的同时计算:每个节点最大子树的节点数。
设 \(size_i\) 为以节点 \(i\) 为根的子树的节点个数。
对于节点 \(i\),最大“向下”子树的节点数则为 \(\max_{j \in Son(i)} {size_j}\),还要和节点 \(i\) 的“向上”子树的节点数作比较,而节点 \(i\) 的“上方子树”的节点数则为 总点数 - 以节点 \(i\) 为根的子树的点数。然后就可以根据定义找出树的重心。
代码
int size[N], d[N], ans; // size[i] 表示 以节点 i 为根的子树的点数,d[i] 表示 节点 i 的最大子树的点数,ans 表示 树的重心。
void DFS(int u, int fa) // u 是 当前点,fa 是 点u的父亲节点。
{
size[u] = 1; // 初始化,以节点 u 为根的子树的点数 要算上 点u 自己。
for (int i = head[u]; ~i; i = edge[i].next)
{
int v = edge[i].to; // 点v 是 点u的子节点
if (v == fa) continue; // 点v 不能是 点u的父节点
DFS(v, u); // 递归搜索
size[u] += size[v]; // 以节点 u 为根的子树的点数 算上 以节点 v 为根的子树的点数
d[u] = max(d[u], size[v]); // 比较出 最大“向下”子树的节点数。
}
d[u] = max(d[u], n - size[u]); // 和节点 u 的“向上”子树的节点数作比较
if (d[u] < d[ans] || (d[u] == d[ans] && u < ans)) ans = u; // 通过比较得出 树的重心。
}
本文作者:T_泓
本文链接:https://www.cnblogs.com/T-hong/p/18321711
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通