【支配树模板】

支配树模板题

注:本文均从网络上摘抄

首先介绍一下什么叫支配树。
1.支配点:
在有向图中,若删除了点x,u到v不连通了,那么称x支配v。
2.支配树:
满足树上一个点x的所有祖先都是它的支配点的树。

下面介绍一下一般有向图的支配树构建方法:
下面化还是一些名词解释:
1.dfs树:
从图的一个点S开始dfs整张图,可以提取出一颗dfs树,并且记x的dfs序为dfn[x]。
2.半支配点:
假设存在一个点y,从y出发有一条到x的路径,并且路径上任意一点z(z!=x && z!=y)都满足 d f n [ z ] > d f n [ x ] dfn[z]>dfn[x] dfn[z]>dfn[x],则称y为x的半支配点。
s e m i [ x ] semi[x] semi[x]为x的dfn最小的半支配点,因为x在dfs树上的父亲也是它的一个半支配点,所以 s e m i [ x ] semi[x] semi[x]一定是x的祖先。并且删掉原图中的非树边后,连边(semi[x],x),不改变原图中的支配点关系。
求出了semi,我们就把图变成了一个DAG,然后就可以重复DAG的做法了(不过我没有讲DAG的做法qwq),下面介绍一种更优的做法。

求半支配点:
对于一个点x,我们找到所有边(y,x)对应的y。
d f n [ y ] < d f n [ x ] dfn[y]dfn[y]<dfn[x] d f n [ y ] dfn[y] dfn[y]比当前找到的semi[x]的dfn小,则 s e m i [ x ] = y semi[x] = y semi[x]=y
d f n [ y ] > d f n [ x ] dfn[y]>dfn[x] dfn[y]>dfn[x],找到树上y的一个祖先z,且 d f n [ z ] > d f n [ x ] dfn[z]>dfn[x] dfn[z]>dfn[x],比较 d f n [ s e m i [ z ] ] dfn[semi[z]] dfn[semi[z]] d f n [ s e m i [ x ] ] dfn[semi[x]] dfn[semi[x]]的大小,决定是否用 s e m i [ z ] semi[z] semi[z]更新 s e m i [ x ] semi[x] semi[x]

从半支配点到支配点:
对于x,我们要求它在支配树上的父亲,也就是 i d o m [ x ] idom[x] idom[x]
方法如下:
记P为从 s e m i [ x ] semi[x] semi[x]到x的树上路径点集(不包括semi[x]),而z是P中 d f n [ s e m i [ z ] ] dfn[semi[z]] dfn[semi[z]]最小的点。若 s e m i [ z ] = = s e m i [ x ] semi[z]==semi[x] semi[z]==semi[x],则有 i d o m [ x ] = s e m i [ x ] idom[x]=semi[x] idom[x]=semi[x],否则有 i d o m [ x ] = i d o m [ z ] idom[x] = idom[z] idom[x]=idom[z]

算法流程:
带权并查集实现即可。

#include 
using namespace std;
const int maxn = 2e5+7;
int n,m,cnt = 0;
int p[maxn],val[maxn],sdom[maxn],idom[maxn],ans[maxn];
int dfn[maxn],id[maxn],fa[maxn];
vector<int> nxt[maxn],pre[maxn],lat[maxn],zps[maxn];
void dfs(int now)
{
	dfn[now] = ++cnt;
	id[cnt] = now;
	for(int i=0;i<nxt[now].size();i++)
	{
		int v = nxt[now][i];
		if(dfn[v]) continue;
		dfs(v);
		fa[v] = now;
	}
}
int find(int x)
{
	if(x==p[x]) return x;
	int rt = find(p[x]);
	if(dfn[sdom[val[p[x]]]]<dfn[sdom[val[x]]]) val[x] = val[p[x]];
	return p[x] = rt;
}
void tarjan()
{
	for(int i=cnt;i>=2;i--)
	{
		int now = id[i];
		for(int j=0;j<pre[now].size();j++)
		{
			int v = pre[now][j];
			if(!dfn[v]) continue;
			find(v);
			if(dfn[sdom[val[v]]]<dfn[sdom[now]]) sdom[now] = sdom[val[v]];
		}
		lat[sdom[now]].push_back(now);
		p[now] = fa[now];
		now = fa[now];
		for(int j=0;j<lat[now].size();j++)
		{
			int v = lat[now][j];
			find(v);
			if(sdom[val[v]]==now) idom[v] = now;
			else idom[v] = val[v];
		}
	}
	for(int i=2;i<=cnt;i++)
	{
		int now = id[i];
		if(idom[now]!=sdom[now]) idom[now] = idom[idom[now]];
	}
}
void gao(int now)
{
	ans[now] = 1;
	for(int i=0;i<zps[now].size();i++)
	{
		int v = zps[now][i];
		gao(v);
		ans[now] += ans[v];
	}
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		nxt[u].push_back(v);
		pre[v].push_back(u);
	}
	for(int i=1;i<=n;i++)
	{
		sdom[i] = p[i] = val[i] = i;
	}
	dfs(1);
	tarjan();
	for(int i=2;i<=n;i++)
	{
		if(idom[i]) zps[idom[i]].push_back(i);
	}
	gao(1);
	for(int i=1;i<=n;i++) printf("%d ",ans[i]);
	puts("");
	return 0;
}

你可能感兴趣的:(算法模板)