[学习笔记]支配树

支配树(DominatorTree)

对于一个流程图,单源有向图,上点\(w\),如果去掉一个点\(d\),使得源点无法到达则称其支配\(w\)

image

通过定义,我们知道支配的这个定义,满足传递性。

定理一:

除源点外所有点均有单一的最近支配点。

且我们知道如果我们找到最近的支配点\((immediate dominator)\),\(idom(w)\),以源点作为祖先,将其构成树,则满足一个点的祖先链上全为其的支配点。

image

支配树相关性质:

我们随便选择一颗生成树。

image

下列的所有比较均为\(dfn\)序比较。

引理一:路径原理:
如果两个点满足\(v \leq w\),那么所有\(v\)\(w\)的路径经过其公共祖先。

符号规定

下列规定一些符号:

\(a\to b\)代表点\(a\)直接经过一条边到达点\(b\)

\(a \rightsquigarrow b\)代表点\(a\)经过某条路径到达点\(b\)

\(a \stackrel{。}{\to} b\)代表经过DFS树到达点\(b\)

\(a \stackrel{+}{\to} b\)代表\(a \stackrel{。}{\to} b\)\(a \neq b\)

定义半支配点

对于\(w\neq r\),他的半支配点\((semi-dominator)\)定义为\(sdom(w) = \min{v|\exists (v_0,v+1,...v_{k - 1},v_k),v_0 = v,v_k = w,\forall(i\neq 0) v_i > w}\)

引理2:对于任意\(w \neq r\),有\(idom(w) \stackrel{+}{\to} w\)

引理3:对于任意\(w \neq r\),有\(sdom(w) \stackrel{+}{\to} w\)

引理4:对于任意\(w \neq r\),有\(idom(w) \stackrel{。}{\to} sdom(w)\)
证明:考虑若不如此,则存在一条\(r \stackrel{。}{\to} sdom(w) \rightsquigarrow w\),与\(idom\)定义矛盾。

引理5:对于满足\(v \stackrel{。}{\to} w\),\(v \stackrel{。}{\to} idom(w)\)\(idom(w) \stackrel{。}{\to} idom(v)\)
证明:如果不是这样:该情况为:\(idom(v) \stackrel{+}{\to} idom(w) \stackrel{+}{\to} v\stackrel{+}{\to} w\),那么存在路径\(r \stackrel{。}{\to} idom(v) \rightsquigarrow v \stackrel{+}{\to} w\),不经过\(idom(w)\)到达了\(w\),因为\(idom(w)\)\(idom(v)\)的真后代,一定不支配\(v\)

前面几条引理较为简单。

后面我们来证明一些较为复杂的定理。

定理二:

image

定理三:

image

推论一:

image

接下里考虑如何快速求出\(sdom\)

定理四:

image

Lengauer-Tarjan算法

算法有四步:
一:先跑出\(DFS\)
二:利用定理四从大到小求出\(sdom\)
三:通过推论一求出所有能确定的\(idom\),否则记录和哪个点的\(idom\)相同
四:从小到大跑出所有的点的\(idom\)

细节:
image

支配树
#include<bits/stdc++.h>
#define ll long long 
#define N 200005

using std::vector;

int n,m;

vector<int>to[N];
vector<int>in[N];
vector<int>buk[N];
int head[N];

int sdom[N],idom[N];

int fa[N];
int eval[N];

//DSU

int dfn[N],cnt;
int inv[N];
int F[N];
int vis[N];
//tree 

inline void dfs(int u){
	vis[u] = 1;
	dfn[u] = ++cnt;
	sdom[u] = dfn[u];
	inv[dfn[u]] = u;
	for(int i = 0;i < to[u].size();++i){
		if(!vis[to[u][i]]){
			int v = to[u][i];
			F[v] = u;
			dfs(v);
		}
	}
}

//sdom : dfn eval : point idom : point

inline int find(int u){
	if(fa[u] == u)return u;
	int f = find(fa[u]);
	if(sdom[eval[fa[u]]] < sdom[eval[u]])
	eval[u] = eval[fa[u]];
	return fa[u] = f; 
}

inline void merge(int x,int y){//x -> y
//	std::cout<<"("<<x<<"->"<<y<<")"<<std::endl;
	fa[x] = y;
}

inline void delta(int u){
	for(;head[u] < buk[u].size();++head[u]){
		int v = buk[u][head[u]];
		int f = find(v);
		if(sdom[eval[v]] == sdom[v])
		idom[v] = u;
		else
		idom[v] = eval[v];
	}
}

inline void del(int u){
	for(int i = 0;i < in[u].size();++i){
		int v = in[u][i];
		int f = find(v);
		sdom[u] = std::min(sdom[u],sdom[eval[v]]);
	}
	buk[inv[sdom[u]]].push_back(u);
	merge(u,F[u]);
	delta(F[u]);
}

//Dom tree

vector<int>A[N];

int siz[N];

inline void serch(int u){
	siz[u] = 1;
	for(int i = 0;i < A[u].size();++i)
	serch(A[u][i]),siz[u] += siz[A[u][i]];
}

int s;

int main(){
	scanf("%d%d%d",&n,&m,&s);
	for(int i = 1;i <= n;++i)
	fa[i] = i,eval[i] = i;
	for(int i = 1;i <= m;++i){
		int x,y;
		scanf("%d%d",&x,&y);
		x ++ ,y ++ ;		
		to[x].push_back(y);
		in[y].push_back(x);
	}
	dfs(s + 1);
	for(int i = n;i >= 2;--i){
		int u = inv[i];
		del(u);//find_sdom
	}
	for(int i = 1;i <= n;++i){
		int u = inv[i];
		if(idom[u] != inv[sdom[u]])
		idom[u] = idom[idom[u]];
	}
	for(int i = 1;i <= n;++i)
	std::cout<<idom[i]<<" ";
}
posted @ 2022-01-11 17:33  fhq_treap  阅读(532)  评论(1编辑  收藏  举报