什么是支配树?
对于一张有向图,确定一个根,如果根到 x 的每条路径都经过 y,那么称 y 是 x 支配点。求出原图的一个 dfs 树,那么 x 的支配点一定在 x 到根的链上。如果每个点向自己深度最深的支配点连边,就构成了支配树。
明确一些有向图 dfs 树的性质:
1.树边总是由 dfn 小的点指向 dfn 大的点,非树边(两端点无祖先关系)总是由 dfn 大的点指向 dfn 小的点。
2.若 v,w 是图中节点且 dfn[v]<=dfn[w],则任意从 v 到 w 的路径必然包含它们在 dfs 树中的一个公共祖先。
如果存在 y 到 x 的一条路径,使得路径上的点(除 x,y)dfn 都大于 x,那么称 y 是 x 的一个半支配点。可以证明 x 的半支配点一定是 x 的祖先,我们记 sdom[x] 为 x 半支配点中深度最小的点。
求 sdom:考虑枚举路径上最后一个点 z, s d o m [ x ] = min { s d o m [ z ] ∣ ( z , x ) ∈ E , d f n [ z ] > d f n [ x ] } sdom[x]=\min\{sdom[z]|(z,x)\in E,dfn[z]>dfn[x]\} sdom[x]=min{sdom[z]∣(z,x)∈E,dfn[z]>dfn[x]}。
怎么求支配点?对于一个点 x,sdom[x] 到 x 之间的点一定不会是支配点。对于 x 到根的每个点 y,把不合法的点删掉后剩下的深度最深的点就是支配点。
实现:
1.求 sdom:相当于求 x 到根的路径上 dfn 大于 dfn[x] 的点的最小值。按 dfn 从大到小处理,带权并查集维护子树。
2.求 idom:相当于求 x 到根的路径上深度大于 sdom[x] 的点的最小值。同样按 sdom[x] 从大到小维护带权并查集。注意桶排。
(可能)比较好写的做法:求完 sdom 以后 sdom[x] 向 x 连边。把图变为 DAG.
洛谷模板:
#include
#define ll long long
using namespace std;
const int N=200010,M=300010;
struct edge{
int to,next;
}ed[M];
int sz,head[N],dfn[N],deep[N],fa[N],size[N],a[N],c[N],tim,sdom[N],idom[N],wb[N],lk[N],tr[M];
vector b[N],d[N];
void add_edge(int from,int to)
{
ed[++sz].to=to;
ed[sz].next=head[from];
head[from]=sz;
}
int read()
{
int x=0;char c=getchar(),flag='+';
while(!isdigit(c)) flag=c,c=getchar();
while(isdigit(c)) x=x*10+c-'0',c=getchar();
return flag=='-'?-x:x;
}
inline int chk(int x,int y)
{
if(!x||!y) return x|y;
if(dfn[x]=1;i--)
{
int x=a[i];
for(int j=0;j=1;i--) wb[i]+=wb[i+1];
for(int i=1;i<=n;i++) a[wb[deep[sdom[i]]]--]=i;
int p=n+1;
for(int i=1;i<=n;i++)
{
int x=a[i];
while(p>deep[sdom[x]])
{
p--;
for(int j=0;j=1;p--)
{
for(int i=0;i