[模板]树的重心

树的重心

定义:找到一个点,其所有的子树中最大的子树节点数最少,那么这个点就是这棵树的重心,删去重心后,生成的多棵树尽可能平衡。

算法流程

首先,利用前向星存边建立边表。由于无向,所以要连两次边。我们用一次 d f s ( ) 建立以1为根节点时每个结点所在子树的结点数。

接下来考虑把这个点删掉的结果,如果一个非根结点有 p 个儿子,那么删掉这个点之后会有 p + 1 个连通分量,因为这个结点不是根节点,那么除了这个结点及它的所有子树外还有一个连通分量,我们将之称为这个结点的上方子树。接下来,计算这些子树(包括上方子树)的 s i z e 再从中找个最大值,因为总共有 n 个结点所以有 n 个最大值,接下来在其中挑一个最小的即可,当这个值取到最小时的那个结点就是这棵树的重心。

s i z e 的计算方法:

  • 结点 u 的子树,直接利用 s i z e 即可。
  • 结点 u 的上方子树,可以利用求补集的思想,总共有 n 个结点,这个结点及其所有子树的结点和为 s i z e ( u ) , 那么它的上方子树的 s i z e 就是 n s i z e ( u )

上述的计算过程都在 d f s 中执行完成,故时间复杂度和空间复杂度均为 O ( n )

代码:

struct Tree {
    struct edgetype {
        int to, next;
    };
    int n, root, maxsize;
    std::vector< edgetype > edge;
    std::vector< int > head, size;
    Tree() : edge(0), head(0), size(0) {}
    Tree(int n) : n(n), head(n + 1, -1), size(n + 1) {}
    inline void AddEdge(int from, int to) {
        edge.push_back((edgetype){to, head[from]});
        head[from] = edge.size() - 1;
    }
    void center(int u, int p) {
        size[u] = 1;
        int ret = 0;
        for (int i = head[u]; i != -1; i = edge[i].next) {
            int v = edge[i].to;
            if (v == p) continue;
            center(v, u);
            size[u] += size[v];
            ret = std::max(ret, size[v]);
        }
        ret = std::max(ret, n - size[u]);
        if (ret < maxsize) {
            maxsize = ret;
            root = u;
        }
    }
};

猜你喜欢

转载自blog.csdn.net/Diogenes_/article/details/80726124