定义
在一个流程图(单源有向图)中,如果从源点到某个点p必须要经过某个点q,则q是p的支配点,我们记除了该点自己外,所有支配点中到它最近的点为该点的最近支配点(记为idom),将所有点与它的最近支配点连边构成的树就是支配树。
求法
对于DAG,非常简单,建出来的支配树也就是灭绝树。
首先建出dfs树,求出每个点的dfs序,之后点的大小比较都根据dfs序。
对于一般流程图则相对复杂,首先定义一下半支配点(记为sdom):
sdom(w)=min{v|∃(v0,v1,⋯,vk−1,vk),v0=v,vk=w,∀1≤i≤k−1,vi>w}
从这个定义不难发现,半支配点要么与该点直接相连,要么通过dfs序大的点绕到该点。
经过思考也不难得到下面的定理:
对于w≠r,令u为v到w的路径上(包含v不包含w,v>w)中sdom(u)最小的一个点,有
若sdom(w)=sdom(u),则idom(w)=sdom(w),反之idom(w)=iodm(u).
对于u点,只要用带权并查集维护一下即可,这样按照dfs序从大到小便可一次求出他们的半支配点,并且顺便求出sdom(w)=sdom(u)时的idom,最后在按dfs序顺次扫一遍,即可求出所有点的支配点。
例题(HDU4694 Important Sisters)
题意
给出一张有向图,求出以n为源点时,所有点的支配点的编号之和。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#define N 50010
#define M 100100
using namespace std;
int n,m,first[N],ans[N],bb,fa[N],fb[N],mn[N],dfn[N],tt,sdo[N],ido[N];
bool vis[N];
struct Bn
{
int to,next;
}bn[M];
vector<int>pre[N],have[N];
inline void add(int u,int v)
{
bb++;
bn[bb].to=v;
bn[bb].next=first[u];
pre[v].push_back(u);
first[u]=bb;
}
int ff(int u)
{
if(u==fb[u]) return u;
int res=ff(fb[u]);
if(sdo[mn[fb[u]]]<sdo[mn[u]]) mn[u]=mn[fb[u]];
return fb[u]=res;
}
inline int ask(int u)
{
ff(u);
return mn[u];
}
void dfs(int now)
{
int p,q;
vis[now]=1;
dfn[++tt]=now;
sdo[now]=tt;
for(p=first[now];p!=-1;p=bn[p].next)
{
if(vis[bn[p].to]) continue;
fa[bn[p].to]=now;
dfs(bn[p].to);
}
}
int main()
{
int i,j,k,p,q;
while(~scanf("%d%d",&n,&m))
{
tt=bb=0;
memset(first,-1,sizeof(first));
memset(vis,0,sizeof(vis));
for(i=1;i<=m;i++)
{
scanf("%d%d",&p,&q);
add(p,q);
}
for(i=1;i<=n;i++) fb[i]=mn[i]=ans[i]=i;
dfs(n);
for(j=tt;j>=2;j--)
{
i=dfn[j];
for(k=0;k<pre[i].size();k++)
if(vis[pre[i][k]])
sdo[i]=min(sdo[i],sdo[ask(pre[i][k])]);
have[dfn[sdo[i]]].push_back(i);
fb[i]=fa[i];
for(k=0;k<have[fa[i]].size();k++)
{
p=have[fa[i]][k];
q=ask(p);
if(sdo[p]==sdo[q]) ido[p]=fa[i];
else ido[p]=q;
}
have[fa[i]].clear();
}
for(j=2;j<=tt;j++)//注意是从2开始
{
i=dfn[j];
if(ido[i]!=dfn[sdo[i]]) ido[i]=ido[ido[i]];
ans[i]+=ans[ido[i]];
}
for(i=1;i<n;i++)
{
if(vis[i]) printf("%d ",ans[i]);
else printf("0 ");
}
printf("%d\n",n);
for(i=1;i<=n;i++) pre[i].clear();
}
}