【图论】树的重心

树的重心的定义是,对树中的某个点,以这个点为根,然后它会拥有若干棵子树,取出最大的那棵子树的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);
}

posted @ 2024-04-10 07:14  purinliang  阅读(5)  评论(0编辑  收藏  举报