注:本文均從網絡上摘抄
首先介紹一下什麼叫支配樹。
1.支配點:
在有向圖中,若刪除了點x,u到v不連通了,那麼稱x支配v。
2.支配樹:
滿足樹上一個點x的所有祖先都是它的支配點的樹。
下面介紹一下一般有向圖的支配樹構建方法:
下面化還是一些名詞解釋:
1.dfs樹:
從圖的一個點S開始dfs整張圖,可以提取出一顆dfs樹,並且記x的dfs序爲dfn[x]。
2.半支配點:
假設存在一個點y,從y出發有一條到x的路徑,並且路徑上任意一點z(z!=x && z!=y)都滿足,則稱y爲x的半支配點。
記爲x的dfn最小的半支配點,因爲x在dfs樹上的父親也是它的一個半支配點,所以一定是x的祖先。並且刪掉原圖中的非樹邊後,連邊(semi[x],x),不改變原圖中的支配點關係。
求出了semi,我們就把圖變成了一個DAG,然後就可以重複DAG的做法了(不過我沒有講DAG的做法qwq),下面介紹一種更優的做法。
求半支配點:
對於一個點x,我們找到所有邊(y,x)對應的y。
若且比當前找到的semi[x]的dfn小,則
若,找到樹上y的一個祖先z,且,比較和的大小,決定是否用更新。
從半支配點到支配點:
對於x,我們要求它在支配樹上的父親,也就是。
方法如下:
記P爲從到x的樹上路徑點集(不包括semi[x]),而z是P中最小的點。若,則有,否則有。
算法流程:
帶權並查集實現即可。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5+7;
int n,m,cnt = 0;
int p[maxn],val[maxn],sdom[maxn],idom[maxn],ans[maxn];
int dfn[maxn],id[maxn],fa[maxn];
vector<int> nxt[maxn],pre[maxn],lat[maxn],zps[maxn];
void dfs(int now)
{
dfn[now] = ++cnt;
id[cnt] = now;
for(int i=0;i<nxt[now].size();i++)
{
int v = nxt[now][i];
if(dfn[v]) continue;
dfs(v);
fa[v] = now;
}
}
int find(int x)
{
if(x==p[x]) return x;
int rt = find(p[x]);
if(dfn[sdom[val[p[x]]]]<dfn[sdom[val[x]]]) val[x] = val[p[x]];
return p[x] = rt;
}
void tarjan()
{
for(int i=cnt;i>=2;i--)
{
int now = id[i];
for(int j=0;j<pre[now].size();j++)
{
int v = pre[now][j];
if(!dfn[v]) continue;
find(v);
if(dfn[sdom[val[v]]]<dfn[sdom[now]]) sdom[now] = sdom[val[v]];
}
lat[sdom[now]].push_back(now);
p[now] = fa[now];
now = fa[now];
for(int j=0;j<lat[now].size();j++)
{
int v = lat[now][j];
find(v);
if(sdom[val[v]]==now) idom[v] = now;
else idom[v] = val[v];
}
}
for(int i=2;i<=cnt;i++)
{
int now = id[i];
if(idom[now]!=sdom[now]) idom[now] = idom[idom[now]];
}
}
void gao(int now)
{
ans[now] = 1;
for(int i=0;i<zps[now].size();i++)
{
int v = zps[now][i];
gao(v);
ans[now] += ans[v];
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
nxt[u].push_back(v);
pre[v].push_back(u);
}
for(int i=1;i<=n;i++)
{
sdom[i] = p[i] = val[i] = i;
}
dfs(1);
tarjan();
for(int i=2;i<=n;i++)
{
if(idom[i]) zps[idom[i]].push_back(i);
}
gao(1);
for(int i=1;i<=n;i++) printf("%d ",ans[i]);
puts("");
return 0;
}