支配树学习笔记

前言

本文参考了:

  • 2020 年信息学奥林匹克中国国家集训队论文:陈孙立,浅谈支配树及其应用

本文和上文在构建支配树的切入点上会有些许不同,尝试用更加容易理解的方式解析支配树的构建。

支配树的存在性及其定义

本文考察的是一个 \(n\) 个点 \(m\) 条边的有向图 \(G=(V,E)\),设其的源为 \(s\),满足 \(s\) 能到达图上的所有点。

定义 \(x\) 支配 \(y\) 当且仅当所有从 \(s\)\(y\) 的路径都会经过 \(x\),称 \(x\)\(y\) 的一个支配点。

显然我们在考察 \(x\) 是否支配 \(y\) 时只需要考虑所有从 \(s\)\(y\) 的简单路径是否都经过 \(x\) 即可,所以我们下文中的 “路径” 在没有特殊说明的情况下指的都是简单路径。

  • 性质 1:若不同的三个点 \(x,y,z\) 满足 \(x,y\) 均支配 \(z\),那么必然满足 \(x\) 支配 \(y\)\(y\) 支配 \(x\)

    证明:不妨设某一条从 \(s\)\(z\) 的路径为 \(s,\dots,x,\dots,y,\dots,z\),若 \(x\) 不支配 \(y\),那么就存在一条不经过 \(x\) 的从 \(s\)\(z\) 的路径,与 \(x\) 支配 \(z\) 矛盾。

根据这条性质,可知对于任意一点 \(x\neq s\) 以及它的支配点集合 \(S=\{y|y\text{ 支配 }x\}\)\(S\) 中的点在支配关系上构成全序集,那么我们找到 \(S\) 中除 \(x\) 以外 “最小” 的元素 \(z\),即满足 \(S\) 中除 \(x\) 外的所有点都支配 \(z\),我们称 \(z\)\(x\) 的直接支配点,记为 \(z=idom(x)\)

再根据支配性的传递性容易证明,\(x\) 的支配点集合等于 \(idom(x)\) 的支配点集合并上 \(\{x\}\)

那么如果我们对于每一个 \(x\neq s\) 都连一条 \(idom(x)\to x\) 的边,容易证明这会构成一棵以 \(s\) 为根的外向树,称为 \((G,s)\) 的支配树。根据上面的推导可知 \(x\) 支配 \(y\) 当且仅当支配树上 \(x\)\(y\) 的祖先。

支配树的构建

对于 DAG 的支配树:我们可以直接拓扑排序,一个点的直接支配点即为它在 DAG 上的所有入点在支配树上的 LCA,倍增维护即可,时间复杂度 \(O((n+m)\log n)\)

下面是对于更一般的图的情况:

考虑从 \(s\) 出发的任意一棵 DFS 生成树,每个点会有一个 DFS 序。为了方便,对于两个点 \(x,y\),我们定义 \(x<y\) 表示 \(x\) 的 DFS 序小于 \(y\) 的 DFS 序,类似地定义 \(x>y\)、点集中的最大点和最小点等概念。

我们用 \(path[x,y]\) 表示树上从 \(x\)\(y\) 的简单路径上的点集,注意区间可开可闭(意为端点是否包含)。

显然一个点的支配点只可能是它在 DFS 树上的祖先。于是对于一个点 \(x\),对于它在 DFS 树上的某个严格祖先 \(y\),我们先考虑判定 \(y\) 是否为 \(x\) 的支配点。

\(sdom(x)\) 表示 \(x\) 的所有满足右述条件的祖先 \(f\) 中深度最小(或 DFS 序最小)的那个:存在一条路径 \(v_0=f,v_1,\dots,v_k=x\) 满足对于任意的 \(i\in [1,k-1]\) 都有 \(v_i\not\in path[f,x]\)

\(sdom(x)=\min\{f|f\text{ 为 }x\text{ 的祖先,且存在一条路径 }v_0=f,v_1,\dots,v_k=x\text{ 满足对于任意的 }i\in[1,k-1]\text{ 都有 }v_i\not\in path[f,x]\}\)

  • 定理 1:对于 \(x\neq s\)\(x\) 的某个严格祖先 \(y\)\(x\) 的支配点等价于 \(\forall v\in path(y,x],sdom(v)\geq y\)

    证明:首先若后者不成立,则前者显然也不成立。然后考虑证明前者不成立必然导致后者不成立。

    前者不成立等价于存在一条从 \(s\)\(x\) 的路径 \(P\) 不经过 \(y\)。设 \(v\)\(P\) 中第一次出现在 \(path(y,x]\) 上的点,\(w\)\(P\)\(v\) 之前最后一次出现在 \(path[s,y)\) 上的点(显然 \(v,w\) 必然存在,因为 \(x,s\) 分别为它们的候选),那么 \(w\) 就是 \(sdom(v)\) 的一个候选点,因为 \(P\)\(w,\dots,v\) 的部分就满足 \(sdom(v)\) 的要求。而且 \(w<y\),于是必然导致 \(y\leq sdom(v)\) 不成立。

    推论\(idom(x)\leq sdom(x)\)

事实上根据定理1,假设我们已经知道了 \(sdom\),我们就已经可以 \(O(n\log n)\) 求出所有点的 \(idom\) 了,具体实现是线段树实现区间覆盖以及线段树上二分。

但我们需要更加优美的做法。

事实上,如果我们深入发掘,还会有一些更强的结论。

  • 引理 1:若 \(v\) 是 dfs 树上 \(w\) 的一个祖先,则 \(v\)\(idom(w)\) 的祖先或 \(idom(w)\)\(idom(v)\) 的祖先。

    \(idom(w)\) 不可能存在于 \(path(idom(v),v)\) 中。

    证明:若 \(idom(w)\) 存在于 \(path(idom(v),v)\) 中,那么根据定义,所有形如 \(s,\dots,v,\dots,w\) 的路径都要经过 \(idom(w)\),即所有形如 \(s,\dots,v\) 的路径都要经过 \(idom(w)\)(否则可以直接再从 \(v\) 沿 dfs 树走到 \(w\) 而不经过 \(idom(w)\)),那么 \(idom(w)\) 也是 \(v\) 的一个支配点,而由于可以沿 dfs 树从 \(idom(v)\) 走到 \(idom(w)\),所以 \(idom(v)\) 不符合直接支配点定义,矛盾。

  • 定理 2:对于 \(w\neq s\),若 \(\forall v\in path(sdom(w),w],sdom(v)\geq sdom(w)\),则 \(idom(w)=sdom(w)\)

    证明:结合定理1及其推论容易证明。

  • 定理 3:对于 \(w\neq s\),设 \(u\)\(path(sdom(w),w]\)\(sdom\) 最小的点,则 \(idom(w)=idom(u)\)

    证明:以下证明建议自己画示意图表示各种点之间的大小关系。

    根据假设可知,\(\forall v\in path(sdom(w),w],sdom(v)\geq sdom(u)\geq idom(u)\)

    根据 \(idom(u)\) 的定义和定理1可知,\(\forall v\in path(idom(u),u],sdom(v)\geq idom(u)\)

    根据 \(sdom(w)<u\) 可将二者结合起来,得到 \(\forall v\in (idom(u),w],sdom(v)\geq idom(u)\),根据定理1,可知 \(idom(u)\)\(w\) 的支配点之一。

    而根据定理1的推论可知 \(idom(w)\leq sdom(w)<u\),再结合引理1可知 \(idom(w)\leq idom(u)\),于是 \(idom(w)=idom(u)\)

根据定理2和定理3,发现若 \(sdom\) 求出,那我们就已经可以求出 \(idom\) 了:因为 \(idom(w)\) 要么等于自己的 \(sdom(w)\),要么等于某个祖先 \(u\)\(idom(u)\),于是按深度(DFS 序)扫一遍即可(详细过程后面会讲)。

于是问题转化为求 \(sdom\)

如果这是一个 DAG,可以直接使用倍增,可惜它并不是。

接下来就是比较神奇的地方,我们将通过等价关系转化 \(sdom\) 的定义。

  • 引理 2:对于非树边中的任意一条横叉边 \(y\to x\),必有 \(y>x\)。证明显然。

    推论 1:若 \(x\leq y\),则任意 \(x\)\(y\) 的路径都必须经过 \(x\)\(y\) 在 dfs 树上的某一个公共祖先。

    证明:若 \(x\)\(y\) 的祖先则显然。否则:把 \(x,y\) 的所有公共祖先都从图中刨去,那么 dfs 树上只剩下若干棵子树且 \(x,y\) 不在同一棵子树。同一棵子树内对应着一段连续的 dfn 区间,通过树边/返祖/后向边我们只能在同一棵子树内移动,而从一棵子树跳到另一棵子树只能通过横叉边,而且只能从 dfn 区间大的子树跳到 dfn 区间小的子树,于是不可能从 \(x\) 跳到 \(y\)

  • 定理 4\(sdom\) 第一种定义:\(sdom(x)=\min\{f|f\text{ 为 }x\text{ 的祖先,且存在一条路径 }v_0=f,v_1,\dots,v_k=x\text{ 满足对于任意的 }i\in[1,k-1]\text{ 都有 }v_i\not\in path[f,x]\}\)。(原来的定义)

    \(sdom\) 第二种定义:\(sdom(x)=\min\{y|\text{ 存在一条路径 }v_0=y,v_1,\dots,v_k=x\text{ 满足对于任意的 }i\in[1,k-1]\text{ 都有 }v_i>x\}\)

    这两种定义是等价的。

    证明:证明分两步走,第一步证明第二种定义下的 \(sdom(x)\) 肯定满足第一种定义下的条件,第二步证明第一种定义下的 \(sdom(x)\) 肯定满足第二种定义下的条件。

    证明第一步:令 \(y\) 为第二种定义下的 \(sdom(x)\)。只需证明 \(y\) 只可能是 \(x\) 的某个祖先即可,此时因为第二个定义中对路径的条件比第一个定义中的更严格,所以 \(y\) 肯定满足第一个定义的条件。

    首先由于 \(x\) 在 dfs 树上的父亲已经满足第二种定义下 \(sdom(x)\) 的要求,所以 \(y<x\)

    那么若 \(y<x\)\(y\) 不是 \(x\) 的祖先,根据引理2的推论可知任意 \(y\)\(x\) 的路径都会经过 \(x\)\(y\) 的某一个共同祖先,DFS 序小于 \(x\),不符合第二种定义下的要求。

    所以 \(y\) 只可能是 \(x\) 的某个祖先。

    证明第二步:令 \(f\) 为第一种定义下的 \(sdom(x)\),设令其满足第一种定义条件的路径为 \(v_0=f,v_1,\dots,v_k=x\),我们只需证明对于任意的 \(i\in [1,k-1]\) 均有 \(v_i>x\) 即可。

    首先 \(v_1\sim v_{k-1}\) 中不可能出现 \(f\) 的祖先,否则我们找到 \(v_1\sim v_{k-1}\) 中最后一个出现的 \(f\) 的祖先,它肯定也满足第一种定义要求,而且它 DFS 序比 \(f\) 小,矛盾。

    考虑若出现了 \(v_i<x\),那么此时 \(v_i\) 不为 \(x\) 的祖先(\(v_i\) 不为 \(f\) 的祖先且不会出现在 \(path[f,x]\) 中),\(x\) 不为 \(v_i\) 的祖先(否则 \(v_i>x\)),由引理2的推论可知 \(v_{i},\dots,v_{k}\) 中肯定会出现 \(v_i,x\) 的某个公共祖先(且它不等于 \(v_i\)\(x\)),矛盾。

新的 \(sdom\) 的定义实际上把限制放松了,这使得我们更容易求 \(sdom\)

  • 定理 5\(sdom(x)\)\(x\) 的所有入边 \((y,x)\) 按如下方式得到的所有候选取最小值得到。

    • 候选为 \(y\)
    • \(y>x\),对于任意 \(z\)\(y\) 在 DFS 树上满足 \(z>x\) 的祖先,\(sdom(z)\) 也都是候选。

    证明:首先候选为 \(y\) 是枚举的 \(k=1\) 的情况。否则若 \(k>1\)

    我们只需要证明 \(sdom(z)\) 一定是一个合法的候选,以及所有合法的候选一定都会被统计到。

    首先 \(sdom(z)\) 一定是一个合法的候选很容易证明:只需要先沿着满足 \(sdom(z)\) 要求的路径从 \(sdom(z)\) 走到 \(z\),路径上除 \(sdom(z)\)\(z\) 以外的点都 \(>z>x\),然后再沿着树边走到 \(y\) 再走到 \(x\) 即为一条满足 \(sdom(x)\) 要求的路径。

    考虑一个合法的候选 \(f\) 以及令其满足 \(sdom(x)\) 条件的路径 \(v_0=f,v_1,\dots,v_k=x\)\(k>1\)),那么 \(y=v_{k-1}\) 肯定会被枚举到,考虑找出 \(v_1,\dots,v_{k-1}\) 中第一次为 \(y\) 的祖先的点 \(z=v_{k'}\),我们可以证明 \(v_1,\dots,v_{k'-1}\)\(>z\):若存在 \(v_i<z\),由引理2的推论可知 \(v_i,\dots,v_{k'}\) 中必须出现 \(v_{k'}\) 的严格祖先,与 \(z\) 的定义矛盾。于是这条满足 \(sdom(x)\) 要求的路径一定会被 \(sdom(z)\) 统计到。

那么我们就可以按 DFS 序从大到小枚举点 \(x\) 并求解 \(sdom(x)\):枚举 \(x\) 的入边 \((y,x)\),先用 \(y\) 更新 \(sdom(x)\),然后若 \(y>x\),找到 \(y\) 往上最高的祖先 \(z\) 仍满足 \(z>x\),那么 \(\forall z'\in path[z,y],z'>x\),于是用 \(path[z,y]\) 上所有点的 \(sdom\)(此时这些点的 \(sdom\) 都已经求好了)更新 \(sdom(x)\)

过程可以用带权并查集维护,时间复杂度 \(O((n+m)\alpha(n))\)

而对于求 \(idom\),让我们先回顾一下描述 \(sdom\)\(idom\) 关系的两个定理:

  • 定理 2:对于 \(w\neq s\),若 \(\forall v\in path(sdom(w),w],sdom(v)\geq sdom(w)\),则 \(idom(w)=sdom(w)\)
  • 定理 3:对于 \(w\neq s\),设 \(u\)\(path(sdom(w),w]\)\(sdom\) 最小的点,则 \(idom(w)=idom(u)\)

显然我们需要先求出 \(path(sdom(w),w]\)\(sdom\) 最小的点 \(u\)(记为 \(mins(w)\)),事实上这可以在求 \(sdom\) 过程中一起处理出来:对于某个点 \(w\) 求出 \(sdom(w)\) 后,把 \(w\) 当做一组询问记录在 \(sdom(w)\) 上,然后在扫到 \(sdom(w)\) 时利用带权并查集求出询问的答案即可(此时 \(w\) 在并查集上的祖先恰好就是 \(path(sdom(w),w]\) 上的所有单)。

最后再按深度(DFS 序)从小往大扫每一个点 \(w\),根据定理2、定理3:若 \(mins(w)=w\),则 \(idom(w)=sdom(w)\);否则 \(idom(w)=idom(mins(w))\)

总时间复杂度 \(O((n+m)\alpha(n))\),空间复杂度 \(O(n+m)\)

P5180【模板】支配树 参考代码:

#include<bits/stdc++.h>

#define N 200010
#define M 300010
#define INF 0x7fffffff

using namespace std;

inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^'0');
		ch=getchar();
	}
	return x*f;
}

int n,m;
int idx,dfn[N],rk[N];
int idom[N],sdom[N],mins[N];

vector<int>in[N],te[N],que[N];

int dmin(int x,int y)
{
	return dfn[x]<dfn[y]?x:y;
}

int smin(int x,int y)
{
	return dfn[sdom[x]]<dfn[sdom[y]]?x:y;
}

namespace DFS
{
	int cnt,head[N],nxt[M],to[M];
	void adde(int u,int v)
	{
		to[++cnt]=v;
		nxt[cnt]=head[u];
		head[u]=cnt;
	}
	bool vis[N];
	void dfs(int u)
	{
		vis[u]=1;
		rk[dfn[u]=++idx]=u;
		for(int i=head[u];i;i=nxt[i])
		{
			int v=to[i];
			if(vis[v]) continue;
			te[u].push_back(v);
			dfs(v);
		}
	}
}

namespace fset
{
	int fa[N],minn[N];
	void init()
	{
		for(int i=1;i<=n;i++) fa[i]=minn[i]=i;
	}
	void find(int x)
	{
		if(x==fa[x]) return;
		find(fa[x]);
		minn[x]=smin(minn[x],minn[fa[x]]);
		fa[x]=fa[fa[x]];
	}
}

int main()
{
	n=read(),m=read();
	for(int i=1;i<=m;i++)
	{
		int u=read(),v=read();
		DFS::adde(u,v);
		in[v].push_back(u);
	}
	DFS::dfs(1);
	fset::init();
	dfn[0]=INF;
	for(int i=n;i>=2;i--)
	{
		int x=rk[i];
		for(int y:in[x])
		{
			sdom[x]=dmin(sdom[x],y);
			if(dfn[y]>dfn[x])
			{
				fset::find(y);
				sdom[x]=dmin(sdom[x],sdom[fset::minn[y]]);
			}
		}
		que[sdom[x]].push_back(x);
		for(int w:que[x])
		{
			fset::find(w);
			mins[w]=fset::minn[w];
		}
		for(int v:te[x]) fset::fa[v]=x;
	}
	for(int w:que[rk[1]])
	{
		fset::find(w);
		mins[w]=fset::minn[w];
	}
	for(int i=2;i<=n;i++)
	{
		int w=rk[i];
		if(mins[w]==w) idom[w]=sdom[w];
		else idom[w]=idom[mins[w]];
	}
	static int size[N];
	for(int i=1;i<=n;i++) size[i]=1;
	for(int i=n;i>=2;i--)
	{
		int u=rk[i];
		size[idom[u]]+=size[u];
	}
	for(int i=1;i<=n;i++)
		printf("%d ",size[i]);
	return 0;
}
posted @ 2022-10-31 09:44  ez_lcw  阅读(86)  评论(0编辑  收藏  举报