支配树

· · 个人记录

此处只记结论,证明可以见这篇博客。

一、问题:

给出一个有向图(可以有环),给定一个根root
对于点对(x,y),如果从rooty的每一条路径都过x,则称x支配y。设idom_x表示离x最近的支配x的点(支配点)。
显然除了根以外每个点xidom_x是唯一的,从idom_xx连边,会得到一棵树,从x的祖先都是x的支配点。

二、求解:

先从root出发求一颗dfs树,并求出每个点xdfsdfn_x

定义半支配点sdom_x表示x的半支配点,其中sdom_x满足存在一条从sdom_xx的路径,对于这条路径上的任意一点(不包括sdom_xxy都满足dfn_y>dfn_x,且sdom_x是满足这个性质的点中dfn最小的那个,显然sdom_x也是唯一的。

1.$sdom_x$是$x$的祖先。 2.$idom_x$是$x$的祖先。 3.$idom_x$是$sdom_x$的祖先。 如何求$sdom_x$: 对于每条边$(x,y)$: $dfn_x<dfn_y$:如果$dfn_x<dfn_{sdom_y}$,那么$sdom_y=x$。 $dfn_x>dfn_y$: 找到$x$的祖先中$dfn$大于$dfn_y$且$dfn$最小的点$z$,用$sdom_z$更新$sdom_y$。 如何求$idom_x$: 我们可以用$sdom$推出$idom$。 1.如果$dfs$树上$sdom_x$到$x$的路径上(不包括$sdom_x$)的所有点$y$都满足$sdom_y\leqslant sdom_x$,那么$idom_x=sdom_x$。 2.不然找到$dfn_{sdom_y}$最小的点$y$,$idom_x=idom_y$。 ### [模板题](https://www.luogu.com.cn/problem/P5180) code: ``` #include<bits/stdc++.h> using namespace std; const int maxn=2e5+10; int n,m,tim; int dfn[maxn],pos[maxn],sdom[maxn],idom[maxn],val[maxn],fa[maxn],bel[maxn]; vector<int>v1[maxn],v2[maxn],v3[maxn]; inline int read() { char c=getchar();int res=0,f=1; while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9')res=res*10+c-'0',c=getchar(); return res*f; } void dfs_pre(int x) { dfn[x]=++tim;pos[tim]=x; for(unsigned int i=0;i<v1[x].size();i++) { int y=v1[x][i]; if(dfn[y])continue; dfs_pre(y);fa[y]=x; } } inline int find(int x) { if(bel[x]==x)return x; int y=find(bel[x]); if(dfn[sdom[val[bel[x]]]]<dfn[sdom[val[x]]])val[x]=val[bel[x]]; return bel[x]=y; } inline void work() { for(int i=tim;i>=2;i--) { int x=pos[i]; for(unsigned int j=0;j<v2[x].size();j++) { int y=v2[x][j]; if(!dfn[y])continue; find(y); if(dfn[sdom[val[y]]]<dfn[sdom[x]])sdom[x]=sdom[val[y]]; } v3[sdom[x]].push_back(x);bel[x]=fa[x];x=fa[x]; for(unsigned int j=0;j<v3[x].size();j++) { int y=v3[x][j]; find(y); if(sdom[val[y]]==x)idom[y]=x; else idom[y]=val[y]; } } for(int i=2;i<=tim;i++) { int x=pos[i]; if(idom[x]!=sdom[x])idom[x]=idom[idom[x]]; } } int cnt_edge; int head[maxn],size[maxn]; struct edge{int to,nxt;}e[maxn]; inline void add_edge(int u,int v) { e[++cnt_edge].nxt=head[u]; head[u]=cnt_edge; e[cnt_edge].to=v; } void dfs_ans(int x) { size[x]=1; for(int i=head[x];i;i=e[i].nxt) { int y=e[i].to; dfs_ans(y);size[x]+=size[y]; } } int main() { n=read(),m=read(); for(int i=1;i<=m;i++) { int u=read(),v=read(); v1[u].push_back(v);v2[v].push_back(u); } for(int i=1;i<=n;i++)sdom[i]=bel[i]=val[i]=i; dfs_pre(1);work(); for(int i=2;i<=n;i++)add_edge(idom[i],i); dfs_ans(1); for(int i=1;i<=n;i++)printf("%d ",size[i]); return 0; } ```