支配树模板,支持计算支配树子树大小,判断两个点在支配树上的祖先关系

发布时间 2023-07-27 17:26:31作者: CECY
#include<bits/stdc++.h>
using namespace std;

struct Dominators {
    const int inf = 0x3f3f3f3f;
    bool isBuild, isCalcDfn, isCalcSiz; // 保证不重复操作的变量 
    int n, tim, parTim, S;
    vector<int> fath, fa, sdom, idom, mn, dfn, pos, siz, firDfn, secDfn;
    vector<vector<int>> orignEdge, rOrignEdge, semiEdge, idomEdge;
    Dominators(int n, int S) : n(n), S(S), 
    fath(n + 1), fa(n + 1), sdom(n + 1), idom(n + 1), mn(n + 1), dfn(n + 1), pos(n + 1),
    orignEdge(n + 1), rOrignEdge(n + 1), semiEdge(n + 1), idomEdge(n + 1) {
        iota(fa.begin(), fa.end(), 0);
        iota(sdom.begin(), sdom.end(), 0);
        iota(mn.begin(), mn.end(), 0);
        isCalcSiz = isCalcDfn = isBuild = parTim = tim = 0;
    }
    // 添加正图边和反图边
    void addGraphEdge(int u, int v) {
        orignEdge[u].push_back(v);
        rOrignEdge[v].push_back(u);
    }
    // 添加半支配树树边
    void addSemiEdge(int u, int v) {
        semiEdge[u].push_back(v);
    }
    // 添加支配树树边
    void addIdomEdge(int u, int v) {
        idomEdge[u].push_back(v);
    }

    void tarjan(int u) {
        dfn[u] = ++tim; pos[tim] = u;
        for(int v : orignEdge[u]) {
            if(!dfn[v]) {
                fath[v] = u;
                tarjan(v);
            }
        }
    }

    int find(int x) {
        if(fa[x] == x) return fa[x];
        int rt = find(fa[x]);
        if(dfn[sdom[mn[fa[x]]]] < dfn[sdom[mn[x]]]) mn[x] = mn[fa[x]];
        return fa[x] = rt;
    }
    // 使用该函数得到直接支配点 
    void dominate() {
        tarjan(S);
        for(int ii = tim; ii >= 2; ii--) {
            int u = pos[ii], res = inf;
            for(int v : rOrignEdge[u]) {
                if(!dfn[v]) continue;
                if(dfn[v] < dfn[u]) {
                    res = min(res, dfn[v]);
                } else {
                    find(v);
                    res = min(res, dfn[sdom[mn[v]]]);
                }
            }
            sdom[u] = pos[res];
            fa[u] = fath[u];
            addSemiEdge(sdom[u], u);

            for(int v : semiEdge[u = fath[u]]) {
                find(v);
                if(u == sdom[mn[v]]) {
                    idom[v] = u;
                } else {
                    idom[v] = mn[v];
                }
            }
            semiEdge[u].clear();
        }
        for(int i = 2; i <= tim; i++) {
            int u = pos[i];
            if(sdom[u] ^ idom[u]) idom[u] = idom[idom[u]];
        }
    }

    void dfsSiz(int u) {
        siz[u] = 1;
        for(int v : idomEdge[u]) {
            dfsSiz(v);
            siz[u] += siz[v];
        }
    }

    void dfsDfn(int u) {
        firDfn[u] = ++parTim;
        for(int v : idomEdge[u]) {
            dfsDfn(v);
        }
        secDfn[u] = ++parTim;
    }
    //  构建支配树
    void buildIdomTree() {
        if(isBuild) return ; isBuild = 1;
        for(int i = 1; i <= n; i++) if(idom[i]) {
            addIdomEdge(idom[i], i);
        }
    }
    // 求每个点可支配的点
    void calcSiz() {
        if(isCalcSiz) return ; isCalcSiz = 1;
        siz = vector<int> (n + 1);
        buildIdomTree();
        dfsSiz(S);
    }
    void calcDfn() {
        if(isCalcDfn) return ; isCalcDfn = 1;
        firDfn = vector<int> (n + 1);
        secDfn = vector<int> (n + 1);
        dfsDfn(S);
    }
    // 判断在支配树上x是不是为y的祖先
    bool isAncestor(int x, int y) {
        buildIdomTree();
        calcDfn();
        return firDfn[x] < firDfn[y] && firDfn[y] < secDfn[x];
        
    }
    // 打印每个点的直接支配点
    void printIdom() {
        for(int i = 1; i <= n; i++) {
            cout << i << " " << idom[i] << "\n";
        }
    }
};