1. 程式人生 > >【模板】樹的重心

【模板】樹的重心

樹的重心

定義:找到一個點,其所有的子樹中最大的子樹節點數最少,那麼這個點就是這棵樹的重心,刪去重心後,生成的多棵樹儘可能平衡。

演算法流程

首先,利用前向星存邊建立邊表。由於無向,所以要連兩次邊。我們用一次 dfs() 建立以1為根節點時每個結點所在子樹的結點數。

接下來考慮把這個點刪掉的結果,如果一個非根結點有 p 個兒子,那麼刪掉這個點之後會有 p+1 個連通分量,因為這個結點不是根節點,那麼除了這個結點及它的所有子樹外還有一個連通分量,我們將之稱為這個結點的上方子樹。接下來,計算這些子樹(包括上方子樹)的 size 再從中找個最大值,因為總共有

n 個結點所以有 n 個最大值,接下來在其中挑一個最小的即可,當這個值取到最小時的那個結點就是這棵樹的重心。

size 的計算方法:

  • 結點 u 的子樹,直接利用 size 即可。
  • 結點 u 的上方子樹,可以利用求補集的思想,總共有 n 個結點,這個結點及其所有子樹的結點和為 size(u), 那麼它的上方子樹的 size 就是 nsize(u)

上述的計算過程都在 dfs 中執行完成,故時間複雜度和空間複雜度均為 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; } } };