注:本文均从网络上摘抄
首先介绍一下什么叫支配树。
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;
}