【圖論】樹的重心

purinliang發表於2024-04-10

樹的重心的定義是,對樹中的某個點,以這個點為根,然後它會擁有若干棵子樹,取出最大的那棵子樹的size,然後在所有點中求最小的最大的子樹的size的點,這個點就是樹的重心。簡單來說就是找到某個點r,去掉r之後,每棵子樹是長得最平均的。樹的重心最多有2個(這時候重心其實是在這兩點之間的邊上),最少有1個。

由於所有的子樹是長得最平均的,而且樹的重心常規情況下都不是葉子,那麼一般都會有多棵子樹。其中最大的一棵子樹的節點數不會超過原樹規模的一半。否則,如果某個子樹超過了原樹規模的一半,那麼除去這棵子樹以外,當前節點和當前節點的其他子樹加起來的數量不夠原樹規模的一半,那麼這時候以這棵子樹的樹根代替當前節點會更好。(聽起來有點像換根dp的思路哈哈)

只找一次重心:

const int MAXN = 2e5 + 10;

int n;
vector<int> G[MAXN];
int center, center_max_siz;
int siz[MAXN];


void find_center (int u, int p) {
    siz[u] = 1;
    int max_siz = 0;
    for (const int &v : G[u]) {
        if (v == p) {
            continue;
        }
        find_center (v, u);
        siz[u] += siz[v];
        cmax (max_siz, siz[v]);
    }
    cmax (max_siz, n - siz[u]);
    if (center == 0 || max_siz < center_max_siz) {
        center = u;
        center_max_siz = max_siz;
    }
}

void solve() {
    RD (n);
    for (int i = 1; i <= n; ++i) {
        G[i].clear();
    }
    for (int i = 1; i <= n - 1; ++i) {
        int u, v;
        RD (u, v);
        G[u].push_back (v);
        G[v].push_back (u);
    }
    center = 0, center_max_siz = INF;
    find_center (1, 0);
    WT (center);
}

多次找重心,每次找到重心之後把當前點去掉,然後在子樹遞迴找重心,複雜度nlogn(就是sum_layer)。每個點被遍歷的次數正比於其layer。

const int MAXN = 2e5 + 10;

int n;
vector<int> G[MAXN];
int center, center_max_siz;
int siz[MAXN];
int layer[MAXN];


void find_center (int u, int p, int cur_n) {
    siz[u] = 1;
    int max_siz = 0;
    for (const int &v : G[u]) {
        if (v == p || layer[v] != 0) {
            continue;
        }
        find_center (v, u, cur_n);
        siz[u] += siz[v];
        cmax (max_siz, siz[v]);
    }
    cmax (max_siz, cur_n - siz[u]);
    if (center == 0 || max_siz < center_max_siz) {
        center = u;
        center_max_siz = max_siz;
    }
}

void find_all_centers (int root, int cur_n, int l) {
    if (layer[root] != 0) {
        return;
    }
    center = 0, center_max_siz = INF;
    find_center (root, 0, cur_n);
    layer[center] = l;
    find_center (center, 0, cur_n);     // 重新計算正確的siz[v]
    for (const int &v : G[center]) {
        find_all_centers (v, siz[v], l + 1);
    }
}

void solve() {
    RD (n);
    for (int i = 1; i <= n; ++i) {
        G[i].clear();
        layer[i] = 0;
    }
    for (int i = 1; i <= n - 1; ++i) {
        int u, v;
        RD (u, v);
        G[u].push_back (v);
        G[v].push_back (u);
    }
    find_all_centers (1, n, 1);

    ll sum_layer = 0;
    for (int i = 1; i <= n; ++i) {
        sum_layer += layer[i];
    }
    WT (sum_layer);
    WTN (layer, n);
}

相關文章