支配树

luogu模版题都是黑的,离谱,瑟瑟发抖

一个有向图,钦点一个点 s 为起点。对于两个点 u,v 当删除点 u 使得没有从 s 到 v 的路径存在时,我们称 u 是 v 的支配点。容易发现,对于这种支配关系可以形成一个树形结构,称之为支配树,支配树的根节点是 s 。

树的支配树就是他自己啦(

DAG

按照拓扑序进行处理,对于一个点 u ,他在支配树中的父亲是所有直接连向他的点 x 的LCA。

有向图

有环了,难搞。

tarjan是真的神仙!!

首先随便弄一个生成树出来,并且标好dfn序。可以发现横叉边只会从dfn大的地方连向dfn小的地方等性质。

定义半支配点:当存在一条路径 \(u \rightarrow v\) ,并且路径上除 u,v 外的 dfn 都小于 \(dfn[v]\) 的时候,我们称 u 是 v 的半支配点。

我们需要求出对于一个点 u 的 dfn 序最小的半支配点 (semi)。

考虑有 \(w \rightarrow u\) 的情况,二者直接相连。

\(dfn[w] < dfn[u]\) 的时候,用 w 来更新 \(semi[u]\)

\(dfn[w] > dfn[u]\) 的时候,用 w 的 semi 以及他部分祖先的 semi 来更新 \(semi[u]\) 。对于可以更新 u 的 x,有 x 是 w 的本身及祖先且 \(dfn[x] > dfn[u]\)

使用带权并查集来进行处理。每次处理完 u 将其连向原dfs树中的father。

有一个性质,我们保留树边和边 \((semi[u] \rightarrow u)\) 后,灭绝树不变。

显然这样处理后是一个 DAG 。可以使用前面提到的 DAG 的做法进行求解即可。

luogu模版code:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>

using namespace std;

int read()
{
	int a = 0,x = 1;char ch = getchar();
	while(ch > '9' || ch < '0') {if(ch == '-') x = -1;ch = getchar();}
	while(ch >= '0' && ch <= '9') {a = a*10 + ch-'0';ch = getchar();}
	return a*x;
}
const int N=1e6+7;
int n,m,head[N],go[N],nxt[N],cnt,anc[N],dfn[N],pos[N],du[N],fa[N],semi[N],mn[N],dp[N],dep[N],jump[N][20],siz[N];
void add(int u,int v)
{
	go[++cnt] = v;
	nxt[cnt] = head[u];
	head[u] = cnt;
}
vector<int>g[N],o[N];
void dfs(int u)
{
	dfn[u] = ++cnt,pos[cnt] = u;
	for(int e = head[u];e;e = nxt[e]) {
		int v = go[e];if(dfn[v]) continue;
		anc[v] = u;g[u].push_back(v);du[v] ++;
		dfs(v);
	}
}

int find(int s)
{
	if(fa[s] == s) return s;
	int tmp = find(fa[s]);
	mn[s] = dfn[semi[mn[s]]]<dfn[semi[mn[fa[s]]]] ? mn[s] : mn[fa[s]];
	fa[s] = tmp;return tmp;
}

int LCA(int a,int b)
{
	if(!a || !b) return a+b;
	if(dep[a] < dep[b]) swap(a,b);
	for(int i = 19;i >= 0;i --) if(dep[jump[a][i]] >= dep[b]) a = jump[a][i];
	if(a == b) return a;
	for(int i = 19;i >= 0;i --) if(jump[a][i] != jump[b][i]) a = jump[a][i],b = jump[b][i];
	return jump[a][0];
}

void DFS(int u)
{
	siz[u] = 1;
	for(int e = head[u];e;e = nxt[e]) {
		int v = go[e];DFS(v);
		siz[u] += siz[v];
	}
}

int main()
{
//	freopen("random.in","r",stdin);freopen("out.out","w",stdout);
	n = read(),m = read();
	for(int i = 1;i <= m;i ++) {
		int u = read(),v = read();
		add(u,v);o[v].push_back(u);
	}
	cnt = 0;dfs(1);
	for(int i = 1;i <= n;i ++) fa[i] = i,semi[i] = mn[i] = i;
	for(int i = n;i >= 2;i --) {
		for(int a:o[pos[i]]) {
			if(!dfn[a]) continue;
			if(dfn[a] < dfn[pos[i]]) semi[pos[i]] = dfn[semi[pos[i]]]>dfn[a]?a:semi[pos[i]];
			else find(a),semi[pos[i]] = dfn[semi[pos[i]]]>dfn[semi[mn[a]]]?semi[mn[a]]:semi[pos[i]];
		}
		g[semi[pos[i]]].push_back(pos[i]);du[pos[i]] ++;fa[pos[i]] = anc[pos[i]];
	}
	for(int i = 1;i <= n;i ++) head[i] = 0;cnt = 0;
	queue<int>q;for(int i = 1;i <= n;i ++) if(!du[i] && dfn[i]) q.push(i);
	while(!q.empty()) {
		int u = q.front();q.pop();
		jump[u][0] = dp[u],dep[u] = dep[dp[u]] + 1;
		if(dp[u]) add(dp[u],u);
		for(int i = 1;i < 20;i ++) jump[u][i] = jump[jump[u][i-1]][i-1];
		for(int v:g[u]) {
			du[v] --;dp[v] = LCA(dp[v],u);
			if(!du[v]) q.push(v);
		}
	}
	DFS(1);
	for(int i = 1;i <= n;i ++) printf("%d ",siz[i]);
	return 0;
}
posted @ 2021-07-09 18:51  nao-nao  阅读(63)  评论(0编辑  收藏  举报