一、感性理解算法
参见https://blog.csdn.net/litble/article/details/83019578
二、算法流程
1.求出图的dfs序(记录:节点所对应dfs序,dfs序所对应节点,父节点)
2.按照dfs序从大到小处理节点
(1)利用带权并查集查询每个父节点的祖先
(带权并查集https://www.cnblogs.com/fenghaoran/p/6898247.html)
(2)求和祖先的sdom比较,求出该节点最小的sdom
(3)在支配树上将其与父亲连边
(4)依次处理父节点的儿子,依照以下两种方式更新
如果sdom[z]==sdom[x] 那么idom[x]=sdom[x]
否则idom[x]=idom[z]
3.处理idom与sdom不等的残余节点
三、代码
洛谷 P5180 【模板】支配树
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=200005,maxm=300005; 4 struct node{ 5 vector<int>edge[maxn]; 6 inline void add(int s,int t){ 7 edge[s].push_back(t); 8 } 9 }form,opp,dfs_tr,idom_tr; 10 int qfa[maxn],fa[maxn],val[maxn]; 11 int ans[maxn],dfn[maxn],id[maxn],cnt; 12 int idom[maxn],sdom[maxn]; 13 int n,m,s,t; 14 void dfs(int x){ 15 dfn[x]=++cnt,id[cnt]=x; 16 for(int i=0;i<form.edge[x].size();i++){ 17 int to=form.edge[x][i]; 18 if(dfn[to])continue; 19 fa[to]=x; 20 dfs(to); 21 } 22 } 23 int find(int x){ 24 if(qfa[x]==x)return x; 25 int fn=find(qfa[x]); 26 if(dfn[sdom[val[qfa[x]]]]<dfn[sdom[val[x]]]) 27 val[x]=val[qfa[x]]; 28 return qfa[x]=fn; 29 } 30 void tarjan(){ 31 for(int x=cnt;x>=2;x--){ 32 int now=id[x]; 33 for(int i=0;i<opp.edge[now].size();i++){ 34 int to=opp.edge[now][i]; 35 if(!dfn[to])continue; 36 find(to); 37 if(dfn[sdom[val[to]]]<dfn[sdom[now]]) 38 sdom[now]=sdom[val[to]]; 39 } 40 dfs_tr.add(sdom[now],now); 41 qfa[now]=fa[now]; 42 now=fa[now]; 43 for(int i=0;i<dfs_tr.edge[now].size();i++){ 44 int to=dfs_tr.edge[now][i]; 45 find(to); 46 if(sdom[val[to]]==now)idom[to]=now; 47 else idom[to]=val[to]; 48 } 49 } 50 for(int i=2;i<=cnt;i++){ 51 int now=id[i]; 52 if(idom[now]!=sdom[now]) 53 idom[now]=idom[idom[now]]; 54 } 55 } 56 void dfs_ans(int x){ 57 ans[x]=1; 58 for(int i=0;i<idom_tr.edge[x].size();i++){ 59 int to=idom_tr.edge[x][i]; 60 dfs_ans(to); 61 ans[x]+=ans[to]; 62 } 63 } 64 int main(){ 65 scanf("%d%d",&n,&m); 66 for(int i=1;i<=m;i++){ 67 scanf("%d%d",&s,&t); 68 form.add(s,t),opp.add(t,s); 69 } 70 for(int i=1;i<=n;i++){ 71 sdom[i]=qfa[i]=val[i]=i; 72 } 73 dfs(1); 74 tarjan(); 75 for(int i=2;i<=n;i++){ 76 if(idom[i]){ 77 // cout<<idom[i]<<endl; 78 idom_tr.add(idom[i],i); 79 } 80 } 81 dfs_ans(1); 82 for(int i=1;i<=n;i++){ 83 printf("%d ",ans[i]); 84 } 85 return 0; 86 }
洛谷 P2597[ZJOI2012]灾难
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=200005,maxm=300005; 4 struct node{ 5 vector<int>edge[maxn]; 6 inline void add(int s,int t){ 7 edge[s].push_back(t); 8 } 9 }form,opp,dfs_tr,idom_tr; 10 int qfa[maxn],fa[maxn],val[maxn]; 11 int ans[maxn],dfn[maxn],id[maxn],du[maxn],cnt; 12 int idom[maxn],sdom[maxn]; 13 int n,m,s,t; 14 void dfs(int x){ 15 dfn[x]=++cnt,id[cnt]=x; 16 for(int i=0;i<form.edge[x].size();i++){ 17 int to=form.edge[x][i]; 18 if(dfn[to])continue; 19 fa[to]=x; 20 dfs(to); 21 } 22 } 23 int find(int x){ 24 if(qfa[x]==x)return x; 25 int fn=find(qfa[x]); 26 if(dfn[sdom[val[qfa[x]]]]<dfn[sdom[val[x]]]) 27 val[x]=val[qfa[x]]; 28 return qfa[x]=fn; 29 } 30 void tarjan(){ 31 for(int x=cnt;x>=2;x--){ 32 int now=id[x]; 33 for(int i=0;i<opp.edge[now].size();i++){ 34 int to=opp.edge[now][i]; 35 if(!dfn[to])continue; 36 find(to); 37 if(dfn[sdom[val[to]]]<dfn[sdom[now]]) 38 sdom[now]=sdom[val[to]]; 39 } 40 dfs_tr.add(sdom[now],now); 41 qfa[now]=fa[now]; 42 now=fa[now]; 43 for(int i=0;i<dfs_tr.edge[now].size();i++){ 44 int to=dfs_tr.edge[now][i]; 45 find(to); 46 if(sdom[val[to]]==now)idom[to]=now; 47 else idom[to]=val[to]; 48 } 49 } 50 for(int i=2;i<=cnt;i++){ 51 int now=id[i]; 52 if(idom[now]!=sdom[now]) 53 idom[now]=idom[idom[now]]; 54 } 55 } 56 void dfs_ans(int x){ 57 ans[x]=1; 58 for(int i=0;i<idom_tr.edge[x].size();i++){ 59 int to=idom_tr.edge[x][i]; 60 dfs_ans(to); 61 ans[x]+=ans[to]; 62 } 63 } 64 int main(){ 65 scanf("%d",&n); 66 for(int i=1;i<=n;i++){ 67 while(1){ 68 scanf("%d",&s); 69 if(s==0)break; 70 du[i]++; 71 form.add(s,i),opp.add(i,s); 72 } 73 } 74 for(int i=1;i<=n+1;i++){ 75 sdom[i]=qfa[i]=val[i]=i; 76 } 77 for(int i=1;i<=n;i++){ 78 if(!du[i]){ 79 form.add(n+1,i),opp.add(i,n+1); 80 } 81 } 82 dfs(n+1); 83 tarjan(); 84 for(int i=1;i<=n;i++){ 85 if(idom[i]){ 86 idom_tr.add(idom[i],i); 87 } 88 } 89 dfs_ans(n+1); 90 for(int i=1;i<=n;i++){ 91 printf("%d\n",ans[i]-1); 92 } 93 return 0; 94 }