灭绝树(支配树)

DAG建立支配树

  1. 原图T的基础上建立TR
  2. 将T进行拓扑排序(若T初始有多个入度为0的结点可以先用一个虚拟根将他们链接起来)
  3. 顺序扫描拓扑排序,假设当前点x,在TR中,x的祖先的支配点已经建立好,所以找到x的所有直接祖先的lca就是x的支配点f。在图D中将f->x连边

有向图支配树建立步骤

  1. 原图E的基础上建立反图G
  2. dfs一次得到dfs序,建立dfs树T
  3. 倒着扫描dfs序上的点x,借助反图和并查集找出semi[x]。将T中连一条semi[x]-> x的边
  4. 此时T为DAG
    1. DAG的基础上进行\(O(nlogn)\) 的算法
    2. 借助已经建立好的并查集和semi求出domi,在D中将domi->x连边
      1. 并查集中权就是并查集代表元素到根节点上的所有点,最小的dfn[sdom[x]]对应的x

最终D就是要求的支配树

参考博客:

[1]https://blog.csdn.net/a710128/article/details/49913553

[2]https://www.cnblogs.com/fenghaoran/p/dominator_tree.html

[3]https://www.luogu.org/problemnew/solution/P5180

模板P5180代码

  1. 求出dfs树+semi之后DAG建支配树
#include <bits/stdc++.h>
using namespace std;
const int N = 3e5+10;
struct Map{
    int head[N],ver[N<<1],nxt[N<<1],cnt;
    void reset(){cnt = 0;memset(head,0,sizeof head);}
    void link(int x,int y){ver[++cnt]=y;nxt[cnt]=head[x];head[x]=cnt;}
}E,G,T,TR,D;
int deg[N],dep[N],dfn[N],id[N],fa[N],f[N][20],semi[N],mm[N],tot,anc[N],ans[N],top[N],cnt,n,m;
void dfs(int x){
    dfn[x] = ++tot;id[tot] = x;
    for(int i=E.head[x];i;i=E.nxt[i]){
        int y = E.ver[i];
        if(dfn[y])continue;
        dfs(y);T.link(x,y);
        //2
        anc[y] = x;
    }
}
int find(int x){
    if(x == fa[x])return x;
    int ff = fa[x];fa[x] = find(fa[x]);
    if(dfn[semi[mm[ff]]] < dfn[semi[mm[x]]])mm[x] = mm[ff];
    return fa[x];
}
int LCA(int x,int y){
    if(dep[x] > dep[y])swap(x,y);
    for(int i=18;i>=0;i--)if(dep[x] < dep[y] && dep[f[y][i]] >= dep[x])y=f[y][i];
    if(x == y)return x;
    for(int i=18;i>=0;i--)if(f[x][i] != f[y][i])x=f[x][i],y=f[y][i];
    return f[x][0];
}
int getAns(int x){
    ans[x] = 1;
    for(int i=D.head[x];i;i=D.nxt[i]){
        int y = D.ver[i];
        getAns(y),ans[x] += ans[y];
    }
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        semi[i] = mm[i] = fa[i] = i;
    }
    for(int i=1,u,v;i<=m;i++){
        scanf("%d%d",&u,&v);
        E.link(u,v);G.link(v,u);
    }
    dfs(1);anc[1] = 0;
    for(int i=n;i>=2;i--){
        int x = id[i],res = n;
        if(!x)continue;
        for(int i=G.head[x];i;i=G.nxt[i]){
            int y = G.ver[i];
            if(!dfn[y])continue;
            if(dfn[y] < dfn[x])res = min(res,dfn[y]);
            else{
                //1.
                find(y);res = min(res,dfn[semi[mm[y]]]);
            }
        }
        semi[x] = id[res];fa[x] = anc[x];
        T.link(semi[x],x);
    }
    for(int x=1;x<=n;x++)
        for(int i=T.head[x];i;i=T.nxt[i]){
            int y = T.ver[i];
            TR.link(y,x);deg[y]++;
        }
    queue<int> q;
    for(int i=1;i<=n;i++)if(deg[i]==0)q.push(i);
    while(!q.empty()){
        int x = q.front();q.pop();
        top[++cnt] = x;
        for(int i=T.head[x];i;i=T.nxt[i]){
            int y = T.ver[i];
            if((--deg[y]) == 0)q.push(y);
        }
    }
    for(int i=1;i<=cnt;i++){
        int x = top[i],bb = -1;
        for(int j=TR.head[x];j;j=TR.nxt[j]){
            int y = TR.ver[j];
            bb = bb==-1?y:LCA(y,bb);
        }
        f[x][0] = bb;D.link(bb,x);dep[x] = dep[bb]+1;
        for(int j=1;j<=18;j++)f[x][j] = f[f[x][j-1]][j-1];
    }
    getAns(1);
    for(int i=1;i<=n;i++)printf("%d ",ans[i]);
    return 0;
}
  1. 求出semi后直接求domi
#include <bits/stdc++.h>
using namespace std;
const int N = 3e5+10;
struct Map{
    int head[N],ver[N<<1],nxt[N<<1],cnt;
    void reset(){cnt = 0;memset(head,0,sizeof head);}
    void link(int x,int y){ver[++cnt]=y;nxt[cnt]=head[x];head[x]=cnt;}
}E,G,T,D;
int dfn[N],id[N],fa[N],f[N][20],semi[N],mm[N],tot,anc[N],ans[N],top[N],cnt,n,m,idom[N];
void dfs(int x){
    dfn[x] = ++tot;id[tot] = x;
    for(int i=E.head[x];i;i=E.nxt[i]){
        int y = E.ver[i];
        if(dfn[y])continue;
        dfs(y);
        anc[y] = x;
    }
}
int find(int x){
    if(x == fa[x])return x;
    int ff = fa[x];fa[x] = find(fa[x]);
    if(dfn[semi[mm[ff]]] < dfn[semi[mm[x]]])mm[x] = mm[ff];
    return fa[x];
}
int getAns(int x){
    ans[x] = 1;
    for(int i=D.head[x];i;i=D.nxt[i]){
        int y = D.ver[i];
        getAns(y),ans[x] += ans[y];
    }
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        semi[i] = mm[i] = fa[i] = i;
    }
    for(int i=1,u,v;i<=m;i++){
        scanf("%d%d",&u,&v);
        E.link(u,v);G.link(v,u);
    }
    dfs(1);anc[1] = 0;
    for(int i=n;i>=2;i--){
        int x = id[i],res = n;
        if(!x)continue;
        for(int i=G.head[x];i;i=G.nxt[i]){
            int y = G.ver[i];
            if(!dfn[y])continue;
            if(dfn[y] < dfn[x])res = min(res,dfn[y]);
            else{
                //1.
                find(y);res = min(res,dfn[semi[mm[y]]]);
            }
        }
        semi[x] = id[res];fa[x] = anc[x];
        T.link(semi[x],x);
        x = anc[x];
        for(int j=T.head[x];j;j=T.nxt[j]){
            int y = T.ver[j];
            find(y);
            if(semi[mm[y]] == x)idom[y] = x;
            else idom[y] = mm[y];
        }
    }
    for(int i=2,now;i<=tot;i++){
        now = id[i];
        if(idom[now] != semi[now])idom[now] = idom[idom[now]];
    }
    for(int i=2;i<=n;i++)if(idom[i])D.link(idom[i],i);
    getAns(1);
    for(int i=1;i<=n;i++)printf("%d ",ans[i]);
    return 0;
}

上面两个实际跑出来时间差不多...另外建图也可以不用结构体,多弄几个head数组即可。但是不能用vector,洛谷测模板题会T

HDU-6604

19年多校的一个题,DAG上面建支配树,结合LCA容斥求支配树上两点的公共点个数

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define pb push_back
const int N = 200010;
//E:反向图,G原图,T支配树
//dep:T中的深度,deg:反向图中的入度
vector<int> E[N],G[N],T[N];
int n,m,deg[N],rt,a[N],dep[N],val[N];
int f[N][20],tot;
void BFS(){
    queue<int> q;
    rt = n+1;
    for(int i=1;i<=n;i++)if(!deg[i]){q.push(i);E[rt].pb(i);G[i].pb(rt);}
    int tot = 0;
    while(!q.empty()){
        int u = q.front();q.pop();
        a[++tot] = u;
        for(int v : E[u])if((--deg[v]) == 0)q.push(v);
    }
}
int LCA(int x,int y){
    if(dep[x] > dep[y])swap(x,y);
    for(int i=19;i>=0;i--)if(dep[y] > dep[x] && dep[f[y][i]] >= dep[x])y = f[y][i];
    if(x == y)return x;
    for(int i=19;i>=0;i--)if(f[x][i] != f[y][i]) x = f[x][i],y=f[y][i];
    return f[x][0];
}
int main(){
    int tt;scanf("%d",&tt);
    while(tt--){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n+1;i++){
            E[i].clear();G[i].clear();T[i].clear();
            dep[i] = deg[i] = 0;
        }
        for(int i=1,u,v;i<=m;i++){
            scanf("%d%d",&u,&v);
            E[v].pb(u);G[u].pb(v);deg[u] ++;
        }
        BFS();
        dep[rt] = 1;
        for(int i=1;i<=n;i++){
            int u = a[i],fa = -1;
            for(int v:G[u])fa = (fa == -1 ?v:LCA(fa,v));
            dep[u] = dep[fa]+1;
            f[u][0] = fa;T[fa].pb(u);
            for(int i=1;i<=19;i++)f[u][i] = f[f[u][i-1]][i-1];
        }
        int q;scanf("%d",&q);
        while(q--){
            int u,v;scanf("%d%d",&u,&v);
            int lca = LCA(u,v);
            printf("%d\n",dep[u] + dep[v] - dep[lca] - 1);
        }
    }
}

猜你喜欢

转载自www.cnblogs.com/1625--H/p/11286941.html