【应用】树上问题-树的重心

树的重心是什么?

对于一棵无根树,设其中的一个节点作为根,则可以形成一棵有根树。

该树以根为分界,分为若干个子树,设其中最大子树具有的节点数为 \(x\)

所有节点里, \(x\) 值最小的节点就是该树的重心,也叫质心。

例如上图这棵树,以1为根时,三个子树的大小分别为3、3、2,其中最大的为3。

1就是这棵树的重心,因为以其他节点为根时,最大子树的大小都会超过3。

比如假设以3为根,则其会有两个子树,分别为{7,8}和{1,2,4,5,6,9},其中最大的子树大小为6。

重心的性质

重心相当于树的平衡点,其尽量均匀地将树分割成了若干部分。

在树分治以及树同构中,重心都具有相当重要的地位。

重心具有几个比较重要的性质:

①一棵树最少有一个重心,最多有两个重心,若有两个重心,则他们相邻(即连有直接边)。

②树上所有点到某个点的距离和里,到重心的距离和最小;若有两个重心,则其距离和相同。

③若以重心为根,则所有子树的大小都不超过整棵树的一半。否则可以通过平移使得最大子树的大小缩小至整树的一半,剩下子树的大小最大为 \(n / 2 - 1\) 。此时新平移到的点才是真正的重心。

④在一棵树上添加或删除一个叶子节点,其重心最多平移一条边的距离。

⑤两棵树通过连一条边组合成新树,则新树重心在原来两棵树的重心的连线上。

如何求解重心?

按其定义来求即可。

我们任选一个节点作为根,然后跑DFS,在过程中记录每个节点各个子树的大小。对于每个节点,其还存在一个上方的子树(即从假设根到当前节点),其大小为总节点数减去当前节点的总子树大小。

然后就能得到每个节点最大子树的节点数,取其中最小值即可。

整个算法只需要遍历一遍树,时间复杂度为 \(O(n)\)

代码实现:

int siz[N], wgt[N];    // size and weight, weight mean max sontree
int n, mi = inf, ans;  // mi=min max size, ans=重心
void dfs(int now, int fa) {
    siz[now] = 1;
    wgt[now] = 0;
    for (int i = 0; i < e[now].size(); i++) {
        int to = e[now][i];
        if (to == fa)
            continue;
        dfs(to, now);
        siz[now] += siz[to];
        wgt[now] = max(wgt[now], siz[to]);
    }
    wgt[now] = max(wgt[now], n - siz[now]);
    if (wgt[now] < mi)
        mi = wgt[now], ans = now;
}
posted @ 2021-01-21 11:12  RioTian  阅读(230)  评论(0编辑  收藏  举报