技术之海一望无际, 记忆长流永无止境|

T_泓

园龄:11个月粉丝:0关注:6

【笔记】 树的重心

定义

对于一棵无根树,找到一个点,使得该树变成以该点为根的有根树时,最大子树的节点数最小,即删除该点后最大联通块的节点数最小。那么该点就是树的重心。

性质

  • 树的重心可能不唯一,但一棵树最多有两个重心,且这两个重心相邻。
  • 以树的重心为根时,所有子树的大小都不会超过整棵树的一半。
  • 树中所有点到某个点的距离和中,到重心的距离和是最小的;如果有两个重心,那么到它们的距离和一样。
  • 把两棵树通过一条边相连得到一棵新的树,那么新的树的重心在连接原来两棵树的重心的路径上。
  • 在一棵树上添加或删除一个叶子,那么其重心最多只移动一条边的距离。

求法

思路

用一次搜索,在将无根树转为有根树的同时计算:每个节点最大子树的节点数。

\(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 中国大陆许可协议进行许可。

posted @   T_泓  阅读(42)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起