【支配樹模板】

支配樹模板題

注:本文均從網絡上摘抄

首先介紹一下什麼叫支配樹。
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)都滿足dfn[z]>dfn[x]dfn[z]>dfn[x],則稱y爲x的半支配點。
semi[x]semi[x]爲x的dfn最小的半支配點,因爲x在dfs樹上的父親也是它的一個半支配點,所以semi[x]semi[x]一定是x的祖先。並且刪掉原圖中的非樹邊後,連邊(semi[x],x),不改變原圖中的支配點關係。
求出了semi,我們就把圖變成了一個DAG,然後就可以重複DAG的做法了(不過我沒有講DAG的做法qwq),下面介紹一種更優的做法。

求半支配點:
對於一個點x,我們找到所有邊(y,x)對應的y。
dfn[y]<dfn[x]dfn[y]<dfn[x]dfn[y]dfn[y]比當前找到的semi[x]的dfn小,則semi[x]=ysemi[x] = y
dfn[y]>dfn[x]dfn[y]>dfn[x],找到樹上y的一個祖先z,且dfn[z]>dfn[x]dfn[z]>dfn[x],比較dfn[semi[z]]dfn[semi[z]]dfn[semi[x]]dfn[semi[x]]的大小,決定是否用semi[z]semi[z]更新semi[x]semi[x]

從半支配點到支配點:
對於x,我們要求它在支配樹上的父親,也就是idom[x]idom[x]
方法如下:
記P爲從semi[x]semi[x]到x的樹上路徑點集(不包括semi[x]),而z是P中dfn[semi[z]]dfn[semi[z]]最小的點。若semi[z]==semi[x]semi[z]==semi[x],則有idom[x]=semi[x]idom[x]=semi[x],否則有idom[x]=idom[z]idom[x] = idom[z]

算法流程:
帶權並查集實現即可。

#include <bits/stdc++.h>
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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章