支配树 | 模板

题目

【模板】支配树

实现思路

  • DFS 一次得到 DfnTid 数组;
  • Dfn 大的点开始枚举点 uu
    • 枚举 uu 的反向邻接点 vv,则 vv 所在的并查集的最小 SDom 可以更新 uu(因为 uu 的 DFS 树上的父亲还没更新进带权并查集,所以这时候所有的 vv 都可以在符合定理的条件下更新 uu),注意不要管不在 DFS 树上的点;
    • SDom 树上将 SDom[u]uu 连边;
    • uu 更新进带权并查集(将自己在并查集上连向自己 DFS 树上的父亲);
    • 此时 uu 所在的子树的 SDom 已经处理好了,于是找到 uu 的 DFS 树的父亲 rr,枚举 rrSDom 树的儿子 vv,用求 IDom 的定理得到 vvIDom 对应的点(不能直接赋成那个点的 IDom,因为那个点可能还没更新)。
  • Dfn 小的点开始枚举点 uu,并把 IDom[u] 由点变为值,即 IDom[u] = IDom[IDom[u]]

代码

#include <bits/stdc++.h>

const int MAXN = 200000;
const int MAXM = 300000;

int N, M;
std::vector<int> G[MAXN + 5], H[MAXN + 5];
std::vector<int> STree[MAXN + 5], ITree[MAXN + 5];

int DfnCnt;
int Fat[MAXN + 5];
int Dfn[MAXN + 5], Tid[MAXN + 5];
int SDom[MAXN + 5],IDom[MAXN + 5];

struct Union_Find {
    int Fat[MAXN + 5], Min[MAXN + 5];

    void Init(int n) {
        for (int i = 1; i <= n; i++)
            Fat[i] = Min[i] = i;
    }

    int Find(int u) {
        if (Fat[u] == u)
            return u;
        int anc = Find(Fat[u]);
        if (Dfn[SDom[Min[u]]] > Dfn[SDom[Min[Fat[u]]]])
            Min[u] = Min[Fat[u]];
        return Fat[u] = anc;
    }
}T;

void Dfs(int u) {
    Tid[Dfn[u] = ++DfnCnt] = u;
    for (int i = 0; i < int(G[u].size()); i++) {
        int v = G[u][i];
        if (!Dfn[v])
            Fat[v] = u, Dfs(v);
    }
}

void Lengauer_Tarjan() {
    for (int i = DfnCnt; i >= 2; i--) {
        int u = Tid[i];
        for (int j = 0; j < int(H[u].size()); j++) {
            int v = H[u][j];
            if (Dfn[v]) {
                T.Find(v);
                if (Dfn[SDom[u]] > Dfn[SDom[T.Min[v]]])
                    SDom[u] = SDom[T.Min[v]];
            }
        }
        STree[SDom[u]].push_back(u);
        T.Fat[u] = Fat[u];
        int r = Fat[u];
        for (int j = 0; j < int(STree[r].size()); j++) {
            int v = STree[r][j];
            T.Find(v);
            if (SDom[T.Min[v]] == r)
                IDom[v] = r;
            else
                IDom[v] = T.Min[v];
        }
    for (int i = 2; i <= DfnCnt; i++) {
        int u = Tid[i];
        if (IDom[u] != SDom[u])
            IDom[u] = IDom[IDom[u]];
    }
}

int Size[MAXN + 5];

void Count(int u) {
    Size[u] = 1;
    for (int i = 0; i < int(ITree[u].size()); i++) {
        int v = ITree[u][i];
        Count(v);
        Size[u] += Size[v];
    }
}

int main() {
    scanf("%d%d", &N, &M);
    for (int i = 1; i <= M; i++) {
        int u, v; scanf("%d%d", &u, &v);
        G[u].push_back(v);
        H[v].push_back(u);
    }
    for (int i = 1; i <= N; i++)
        SDom[i] = i;
    T.Init(N);
    Dfs(1);
    Lengauer_Tarjan();
    for (int i = 2; i <= N; i++)
        if (IDom[i])
            ITree[IDom[i]].push_back(i);
    Count(1);
    for (int i = 1; i <= N; i++)
        printf("%d ", Size[i]);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章