支配树
支配关系
给定一张有向图,钦定一个入口 \(s\),对于一个节点 \(u\), 若从 \(s\) 到 \(u\) 的每一条路径都经过某一个节点 \(v\) ,则我们称 \(v\) 支配 \(u\),记作 $v,\text{dom},u $,注意对于 \(s\) 不能到达的结点,其支配关系是无意义的,因此我们默认 \(s\) 能到达图上的所有节点
引理 1: \(s\) 是所有节点的支配点,任意一个节点都是自己的支配点
引理 2:仅考虑简单路径得出的支配关系与考虑所有路径得出的关系相同。
引理 3:如果 \(u\,\text{dom}\,v\),\(v\,\text{dom}\,w\) 则 \(u\,\text{dom}\,w\)
引理 4:如果 \(u\,\text{dom}\,v\),\(v\,\text{dom}\,u\),则 \(u=v\)
引理 5:若 \(u,v,w\) 两两不同,\(u\,\text{dom}\,w\) 且 \(v\,\text{dom}\,w\),则 \(u,v\) 之间一定存在支配关系
由上述引理,现对于所有除 \(s\) 之外的点 \(x\),我们考虑点集 \(S=\left\{y|y 支配 x\right\}\),则 \(S\) 中至少存在两个元素,因此一定存在一个点 \(z\) 满足 :如果 \(y\) 支配 \(x\) ,\(y\) 也支配 \(z\)。我们称 \(z\) 是 \(x\) 的直接支配点,记为 \(z=idom(x)\).
如果我们对于所有 \(x\) 连一条 \(idom(x)\rightarrow x\) ,则原图构成一颗 以 \(s\) 为根的树型结构,若 \(u\,\text{dom}\,v\) 当且仅当 \(u\) 是 \(v\) 的祖先,我们称此树型结构为支配树
支配树的求解
特殊图的支配树求解
1.树型图的支配树是其本身
2.DAG上,我们可以根据拓扑序求解,且前求得的解不会对后面产生影响
引理 6 ,在 DAG 上 点 \(y\,\text{dom}\,x\) 当且仅当 \(y=x\) 或 \(y\,\text{dom}\,S\) 其中 \(S=\left\{v|存在点 v\rightarrow x\right\}\)
反证法即可
换句话说,假设我们遍历到 \(x\) 节点时 ,已经构建了一颗包含之前所有节点的支配树,则\(idom(x)=LCA(idom(v_1)··· idom(v_n))\) ,相当于在支配树上加一个叶子,用倍增维护即可
3.一般图的支配树
首先有一个显然的 \(O(n^2)\) 做法,依次枚举删除哪个节点,用bfs 算出这个点支配多少点,则对于一个点,在能支配它的点中,被支配的集合最大的点即为 \(idom\)
为了更快的求解支配树,我们需要考虑更快的算法
之后的算法中我们要考虑任意一个图 \(G\) 的从 \(s\) 出发的 \(\text{DFS}\) 树,即进行深度优先遍历时经过的边形成的树结构,同时按照遍历顺序( \(\text{DFS}\) 序)为点赋予大小。具体来说对于两点 \(x\) , \(y\) ,如果 \(x\) 在遍历时比 \(y\) 访问的时间(即 \(\text{dfn}\) 序)更早,则称\(x < y\) ,类似地也可定义 \(x > y\) ,以及点集的最大值和最小值等相关概念,注意下文的所有大小关系均指 \(\text{dfn}\) 序上的大小关系
定理 1 如果 \(x\leq y\) ,则任意 \(x\) 到 \(y\) 的路径必须经过 \(x\) 和 \(y\) 在 \(\text{DFS}\) 树上的某个公共祖先
当 \(x\) 为 \(y\) 的祖先时结论显然,假设 \(x\) 不是 \(y\) 的祖先,假设存在一条路径 \(x = v_0, v_1, . . . , v_k = y\) ,其中不存在 \(x\) 和 \(y\) 在 \(\text{DFS}\) 树上的公共祖先。令 \(z\) 是 \(x\) 和 \(y\) 在 \(\text{DFS}\) 树上的最近公共祖先,\(z\) 的孩子中必有唯一一点 \(y′\) 是 \(y\)
的祖先,令 \(p=\max\left\{i|v_i<y^{'}\right\}\),假设其不是 \(z\) 的祖先 ,则 \(v_p\) 一定不是 \(v_{p+1}\) 的祖先,但又因为存在 \((v_p,v_{p+1})\) ,这与不是祖先矛盾,则 \(v_p\) 一定是 \(z\) 的祖先
下面定义半支配点
点 \(x\) 的 半支配点 \(sdom(x)=\min\left\{y|存在一条路径 y=v_0,v_1,...v_n=x\right\}\) ,满足对于任意的 \(1\leq i\leq k-1\) ,都有 \(v_i>x\)
引理 7 \(idom(x)\) 和 \(sdom(x)\) 都是 \(\text{DFS}\) 树上 \(x\) 的祖先且不等于 \(x\) ,更进一步的 ,在 \(\text{DFS}\) 树上的 \(idom(x)\) 是 \(sdom(x)\) 的祖先
证明 : \(idom(x)\) 显然,而 \(\text{DFS}\) 树上的父亲已经满足半支配点的祖先,所以 \(sdom(x)\) 也为 \(x\) 的祖先,而根据半支配点的定义,其选择的路径与 \(\text{DFS}\) 树上的路径完全不同,所以在 \(\text{DFS}\) 树上的路径从 \(sdom(x) 到 x\) 之间(不包含端点)都不支配 \(x\),所以\(idom(x)\) 是 \(sdom(x)\) 的祖先
引理 8,令 \(v, w\) 满足 \(\text{DFS}\) 树上 \(v\) 是 \(w\) 的祖先,则 \(v\) 是 \(idom(w)\) 的祖先或 \(idom(w)\) 是 \(idom(v)\) 的祖先
证明:因 此 假 设 \(v\) 和 \(w\) 不相同 。 使 用 反 证 法, 由 于\(v, w, idom(v), idom(w)\) 都是 \(w\) 的祖先,如果引理中的结论不成立,则它们按深度排序依次是 $ idom(v), idom(w), v, w$ 且它们互不相等。这意味着存在一条不经过 \(idom(w)\) 的从 \(s\) 到 \(v\) 的路径,继续沿 \(\text{DFS}\) 树走到 \(w\) 就得到一条不经过\(idom(w)\) 的从 \(s\) 到 \(w\) 的路径,矛盾
由上我们可以得出一个十分重要的定理
定理 2 \(sdom(u)=\min(\left\{v|存在v\rightarrow u 且 v<u\right\}\cup\left\{sdom(w)|w 是 v 在 DFS 树上的祖先,w > u 且有边 (v, u) \right\})\)
证明 令 \(x\) 等于右式
首先由 \(sdom(u)\leq x\) ,前半部分显然为一个半支配点的候选集合,后半部分我们考虑将定义中 \(sdom(w)\) 到 \(w\)
的路径接上 \(\text{DFS}\) 树上的路径再接上 \((v,u)\) 这条边,则这条边显然是一条半支配点路径
还需证明 \(sdom(u)\geq x\) ,考虑 \(sdom(w)\) 到 \(w\) 的半支配点对应的路径 \(v_0=sdom(w),v_1,...,v_k=w\) ,设 \(k>1\) ,取最小的 \(j\geq 1\) 且 \(v_j\) 是 \(v_{k-1}\) 在 \(\text{DFS}\) 树上的祖先的数,则 \(j\) 一定存在
考虑路径 \(v_0,...v_j\) ,我们断定它是一条满足半支配点的路径,若不是,考虑取\(v_i\leq v_j\) 中 \(v_i\) 最小的 \(i\) ,则一定有定理 1 得其一定为 \(v_j\) 得祖先,与 \(j\) 得选取矛盾,所以断言成立,则有 \(sdom(v_j)\leq sdom(w)\) 而 \(sdom(v_j)\) 在情况二中被考虑到 ,因此证明成立
至此,我们可以利用带权并查集以 \(O((n+m)\log n)\) 的时间快速维护每个点的半支配点
半支配点关于支配点有着十分优美的性质,具体如下
定理二 任取点 \(w\ne s\) 。考虑 \(\text{DFS}\) 树上从 \(sdom(w)\) 到 \(w\) 的路径上(不包括 \(sdom(w)\) )的任意点 \(x\),如果均满足 \(sdom(x) ≥ sdom(w)\) ,则 \(idom(w) = sdom(w)\)
证明 由 引理 7 可得 \(idom(w)\) 为 \(sdom(w)\) 的祖先,我们只需证明 \(sdom(w)\) 支配 \(w\),考虑任意一条 \(s\) 到 \(w\) 的路径 \(P\),我们应证明 \(sdom(w)\) 一定在 \(P\) 中 ,令 \(x\) 为 \(P\) 中最后一个满足 \(x<sdom(w)\) 的点,若不存在则必有 \(sdom(w)=idom(w)=s\) 。若存在,令 \(y\) 为 \(P\) 中 \(x\) 之后的第一个在 \(DFS\) 树中从 \(sdom(w)\) 到 \(w\) 路径上的点,并在 \(P\) 中截取 \(x\) 到 \(y\) 之间的部分 \(v_0=x,v_1,...v_k=y\)
考虑该路径,我们声称 \(1\leq i<k\) 都有 \(v_i>y\),即 \(sdom(y)\leq x\),假使不满足,则有 \(v_i\leq y\) ,由定理 1可知一定存在 \(i\leq j\leq k-1\) 满足 \(v_j\) 是 \(y\) 的祖先,由 \(x\) 的取值可知 \(sdom(w)\leq v_j\),于是有 \(v_j\) 也在 \(sdom(w)\) 到 \(w\) 的路径上,与 \(y\) 的取值矛盾。所以可知 \(sdom(y)\leq x <sdom(w)\) 而定理条件有\(sdom(x) ≥ sdom(w)\),所以 \(y\) 一定为 \(sdom(w)\)
定理三 任取点 \(w \ne s\) 。考虑 \(\text{DFS}\) 树上从 \(sdom(w)\) 到 \(w\) 的路径上(不包括 \(sdom(w)\)的所有点,令 \(u\) 是其中 \(sdom\) 最小的点,则 \(sdom(u) ≤ sdom(w)\) 且\(idom(u) = idom(w)\) 。
证明 ,由于 \(w\) 也是 \(u\) 的一个候选,则 \(sdom(u)\leq sdom(w)\),注意到 \(idom(w)\) 是 \(u\) 的祖先,有引理 8 可知 \(idom(w)\) 也是 \(idom(u)\) 的祖先,只需证明 \(idom(u)\) 支配 \(w\),考虑一条 \(s\) 到 \(w\) 的路径 \(P\) ,我们将要证明 \(idom(u)\) 一定在 \(P\) 中,令 \(x\) 是 \(P\) 中最后一个满足 \(x<idom(u)\) 的点,若不存在则必\(idom(w)=idom(w)=s\) 。若存在,令 \(y\) 为 \(P\) 中 \(x\) 之后的第一个在 \(DFS\) 树中从 \(idom(w)\) 到 \(w\) 路径上的点,并在 \(P\) 中截取 \(x\) 到 \(y\) 之间的部分 \(v_0=x,v_1,...v_k=y\)
和定理 2 的证明类似,仍然有 \(sdom(y)\leq x\) ,有引理 2 可知有 \(idom(u)\leq sdom(u)\) ,即有 \(sdom(y)\leq x<idom(u)<sdom(u)\),可知 \(y\) 不能是 \(sdom(w)\) 的除自身外的后代;另一方面,\(y\) 不能既是 \(idom(u)\) 的除自身外后代也是 u 的祖先,否则沿 DFS 树从 \(s\) 走到 $sdom(y) $再沿 P 走到 y,最后沿 \(\text{DFS}\) 树走到\(u\) 的这条路径就不经过$ idom(u)$ 了,这和支配点的定义矛盾,则情况只剩下了 \(y=idom(u)\) 了
复杂的证明,其实总结起来就几句话
1.\(sdom(u)\)的计算式
\(sdom(u)=\min(\left\{v|存在v\rightarrow u 且 v<u\right\}\cup\left\{sdom(w)|w 是 v 在 DFS 树上的祖先,w > u 且有边 (v, u) \right\})\)
2.\(idom(u)\) 的计算式(令\(v\) 为 \(sdom(u)\) 和 \(u\) 之间,\(sdom(v)\) 最小的节点)
\(idom(u)=\begin{cases}sdom(u)&sdom(v)\geq sdom(u)\\idom(v)&sdom(v)<sdom(u)\end{cases}\)
这就可以写代码了
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
char buf[1<<21],*p1,*p2;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
long long rd()
{
long long res=0,f=1;char ch;
for(ch=getchar();ch<'0'||ch>'9';ch=getchar())
if(ch=='-') f=-1;
for(;ch>='0'&&ch<='9';ch=getchar())
res=(res<<3)+(res<<1)+ch-'0';
return res*f;
}
void wt(int x,char ch=0)
{
if(x<0) putchar('-'),wt(-x);
else
{
if(x>=10) wt(x/10);
putchar(x%10+'0');
}
if(ch) putchar(ch);
return ;
}
struct edge{
int to,nex;
}e[840010];
int head[500100],cnt;
void add(int u,int v)
{
e[++cnt]={v,head[u]};
head[u]=cnt;
}
int n,m,dfncnt;
int ans[500100],dfn[500100],rnk[500100],sdm[500100],idm[500100],fa[500100],mn[500100];
int f[500100];
vector<int>vc[2][500100];
void dfs(int now,int fath)
{
dfn[now]=++dfncnt;
rnk[dfncnt]=now;
f[now]=fath;
for(int i=head[now];i;i=e[i].nex)
{
int v=e[i].to;
if(!dfn[v])
{
dfs(v,now);
}
}
}
int find(int x)
{
if(x==fa[x]) return fa[x];
int tmp=fa[x];
fa[x]=find(fa[x]);
if(dfn[sdm[mn[tmp]]]<dfn[sdm[mn[x]]])
{
mn[x]=mn[tmp];
}
return fa[x];
}
void tar(int st)
{
dfs(st,0);
for(int i=1;i<=n;i++)
{
mn[i]=fa[i]=sdm[i]=i;
}
for(int i=dfncnt;i>=2;i--)
{
int u=rnk[i],res=0x3f3f3f3f;
for(int j=0;j<(int)(vc[0][u].size());j++)
{
int v=vc[0][u][j];
if(!dfn[v]) continue;
find(v);
if(dfn[v]<dfn[u])
{
res=min(res,dfn[v]);
}
else
{
res=min(res,dfn[sdm[mn[v]]]);
}
}
sdm[u]=rnk[res];
fa[u]=f[u];
vc[1][sdm[u]].push_back(u);
u=f[u];
for(int j=0;j<(int)vc[1][u].size();j++)
{
int v=vc[1][u][j];
find(v);
if(dfn[sdm[mn[v]]]>=dfn[u])
{
idm[v]=u;
}
else idm[v]=mn[v];
}
vc[1][u].clear();
}
for(int i=2;i<=dfncnt;i++)
{
int u=rnk[i];
if(idm[u]!=sdm[u])
{
idm[u]=idm[idm[u]];
}
}
for(int i=dfncnt;i>=2;i--)
{
++ans[rnk[i]];
ans[idm[rnk[i]]]+=ans[rnk[i]];
}
ans[1]++;
}
signed main()
{
// freopen("txt.in","r",stdin);
// freopen("txt.out","w",stdout);
// ios::sync_with_stdio(false); cin.tie(0);
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int u,v;
cin>>u>>v;
add(u,v);
vc[0][v].push_back(u);
}
tar(1);
for(int i=1;i<=n;i++)
{
cout<<ans[i]<<" ";
}
return 0;
}
例题