灵魂滚烫, 命运冰凉|

fhq_treap

园龄:5年7个月粉丝:67关注:21

[学习笔记]支配树

支配树(DominatorTree)

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

image

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

定理一:

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

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

image

支配树相关性质:

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

image

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

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

符号规定

下列规定一些符号:

ab代表点a直接经过一条边到达点b

ab代表点a经过某条路径到达点b

ab代表经过DFS树到达点b

a+b代表abab

定义半支配点

对于wr,他的半支配点(semidominator)定义为sdom(w)=minv|(v0,v+1,...vk1,vk),v0=v,vk=w,(i0)vi>w

引理2:对于任意wr,有idom(w)+w

引理3:对于任意wr,有sdom(w)+w

引理4:对于任意wr,有idom(w)sdom(w)
证明:考虑若不如此,则存在一条rsdom(w)w,与idom定义矛盾。

引理5:对于满足vw,vidom(w)idom(w)idom(v)
证明:如果不是这样:该情况为:idom(v)+idom(w)+v+w,那么存在路径ridom(v)v+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]<<" ";
}

本文作者:fhq_treap

本文链接:https://www.cnblogs.com/dixiao/p/15789283.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   fhq_treap  阅读(538)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起