灭绝树(支配树)
Posted 1625--h
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了灭绝树(支配树)相关的知识,希望对你有一定的参考价值。
DAG建立支配树
- 原图T的基础上建立TR
- 将T进行拓扑排序(若T初始有多个入度为0的结点可以先用一个虚拟根将他们链接起来)
- 顺序扫描拓扑排序,假设当前点x,在TR中,x的祖先的支配点已经建立好,所以找到x的所有直接祖先的lca就是x的支配点f。在图D中将f->x连边
有向图支配树建立步骤
- 原图E的基础上建立反图G
- dfs一次得到dfs序,建立dfs树T
- 倒着扫描dfs序上的点x,借助反图和并查集找出semi[x]。将T中连一条semi[x]-> x的边
- 此时T为DAG
- DAG的基础上进行\\(O(nlogn)\\) 的算法
- 借助已经建立好的并查集和semi求出domi,在D中将domi->x连边
- 并查集中权就是并查集代表元素到根节点上的所有点,最小的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代码
- 求出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;
- 求出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);
以上是关于灭绝树(支配树)的主要内容,如果未能解决你的问题,请参考以下文章