1. 程式人生 > 實用技巧 >有向圖的支配樹

有向圖的支配樹

支配樹

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;
}