有向圖的支配樹
阿新 • • 發佈:2020-11-18
支配樹
DGA
思路:
求支配樹的每個節點的子樹大小(不包含自己)
#include<bits/stdc++.h> #define _ 565535 using namespace std; int n,rd[_],A[_],dep[_],st[_][21]; vector<int>prey[_],Top[_],tr[_]; int get_lca(int a,int b){ if(dep[a]<dep[b])swap(a,b); for(int i=20;~i;--i)if(dep[st[a][i]]>=dep[b])a=st[a][i]; if(a==b)return a; for(int i=20;~i;--i)if(st[a][i]!=st[b][i])a=st[a][i],b=st[b][i]; return st[a][0]; } void solve(int x){ int lca=prey[x][0]; for(int i=1,l=prey[x].size();i<l;i++) lca=get_lca(lca,prey[x][i]); tr[lca].push_back(x); dep[x]=dep[lca]+1; st[x][0]=lca; for(int i=1;st[st[x][i-1]][i-1];i++) st[x][i]=st[st[x][i-1]][i-1]; } void topo(){ queue<int>q; for(int i=1;i<=n;i++) if(!rd[i]) Top[0].push_back(i),prey[i].push_back(0),rd[i]++; q.push(0); while(!q.empty()){ int u=q.front();q.pop(); for(int i=0,l=Top[u].size();i<l;i++){ int v=Top[u][i]; rd[v]--; if(rd[v]==0)q.push(v),solve(v); } } } void calc(int u){ A[u]=1; for(int i=0,l=tr[u].size();i<l;i++){ int v=tr[u][i]; calc(v); A[u]+=A[v]; } } int main(){ cin>>n; for(int i=1,x;i<=n;i++) while(1){ scanf("%d",&x); if(x)Top[x].push_back(i),rd[i]++,prey[i].push_back(x); else break; } topo(); calc(0); for(int i=1;i<=n;i++)printf("%d\n",A[i]-1); return 0; }
一般的有向圖
思路:
#include<bits/stdc++.h> #define N 500008 #define M 2000000 #define IL inline using namespace std; /*-------------OI使我快樂-------------*/ int n,m,tot,cnt; int head[M<<1],pre[N<<1],to[N<<2],nex[N<<2],lat[M],cdy[M]; int bel[M],val[M],sdom[M],idom[M],ans[M]; int dfn[M],id[M],fa[M]; IL void add(int *h,int x,int y) { to[++tot]=y; nex[tot]=h[x]; h[x]=tot; } IL void dfs(int now) { dfn[now]=++cnt; id[cnt]=now; for(int i=head[now]; i; i=nex[i]) { int v=to[i]; // printf("%d --> %d\n",now,v); if(dfn[v]) continue; dfs(v); fa[v]=now; } } IL int find(int x) { if(x==bel[x]) return x; int root=find(bel[x]); if(dfn[sdom[val[bel[x]]]]<dfn[sdom[val[x]]]) val[x]=val[bel[x]]; return bel[x]=root; } IL void Tarjan() { // for(R int i=1;i<=n;++i) printf("dfn[%d] %d\n",i,dfn[i]); for(int i=cnt; i>=2; --i) { int now=id[i]; //我們按照dfs序 從大到小處理 節點 for(int j=pre[now]; j; j=nex[j]) { //這裡用鏈式前向星 存入度節點 int v=to[j]; if(!dfn[v]) continue; find(v); //dfn[y]<dfn[x]的話 //我們還未處理到 sdom[y]=val[y]=y //此時y的並查集樹中只有TA自己 更新是合法的 //dfn[y]>dfn[x]的話 //就按照套路比較 if(dfn[sdom[val[v]]]<dfn[sdom[now]]) sdom[now]=sdom[val[v]]; } add(lat,sdom[now],now); bel[now]=fa[now]; //同其在dfs樹上的父親連邊 now=fa[now]; //現在父親到TA這棵子樹已經處理完了 //所以此時我們可以對 //以父親為sdom[x]的x分別求一次sdom 然後清空 //對於每一個點進行更新 for(int j=lat[now]; j; j=nex[j]) { int v=to[j]; find(v); if(sdom[val[v]]==now) idom[v]=now; //如果sdom[z]==sdom[x] 那麼idom[x]=sdom[x] //此時sdom[x]=now else idom[v]=val[v]; //否則就是idom[x]=idom[z] //但是實際實現我們可以寫成idom[x]=z //然後在接下來處理 } } for(int i=2,now; i<=cnt; ++i) { now=id[i]; if(idom[now]!=sdom[now]) idom[now]=idom[idom[now]]; //這是殘餘節點的填坑大作戰 } } IL void dfs_ans(int now) {//支配樹 ans[now]=1; for(int i=cdy[now]; i; i=nex[i]) { int v=to[i]; dfs_ans(v); ans[now]+=ans[v]; } } int main() { // freopen(".in","r",stdin); // freopen(".out","w",stdout); scanf("%d%d", &n, &m); for(int i=1,x,y; i<=m; ++i) { scanf("%d%d", &x, &y); add(head,x,y); add(pre,y,x); } for(int i=1; i<=n; ++i) sdom[i]=bel[i]=val[i]=i; dfs(1); Tarjan(); /* puts("How old are you ? "); for(int i=1;i<=n;++i) { printf("支配點[%d] %d\n",i,idom[i]); } */ tot=0; for(int i=2; i<=n; ++i) if(idom[i]) add(cdy,idom[i],i); dfs_ans(1); for(int i=1; i<=n; ++i) printf("%d%c",ans[i],(i==n ? '\n':' ')); // fclose(stdin); // fclose(stdout); return 0; }