支配树

DAG 支配树

对DAG进行拓扑排序。(u) 的支配树上父亲节点即 (u) 的所有前驱在支配树上的 lca。

CF757F Team Rocket Rises Again

建出最短路图的支配树,题目问的即树上最大真子树。

普通有向图的支配树 Lengauer Tarjan

考虑一张普通有向图的支配树。我们进行一些定义

  • (s_u) 半支配点。(s_u)(dfn) 最小的点 (v) 满足 (v o u) 有一条路径满足除了 (v,u) 剩余点的 (dfn)(> dfn_u)
  • (d_u) 支配点。没啥好定义的。

对于边 (v o u),考虑两种情况

  1. (dfn_v<dfn_u)(v) 可能是 (u) 的半支配点(路径:(v o u))。
  2. (dfn_v>dfn_u)(v) 的祖先 (w) 的半支配点 (s_w) 可能是 (u) 的半支配点(路径:(s_w o w o v o u))。

我们考虑由 dfn 从大往小做。对于一个点 (u),遍历 (v o u),考虑两种情况。第一种情况好做,第二种情况考虑维护一个带权并查集, 维护 (v) 的已经访问过的祖先半支配点 (dfn) 最小的 (w),然后用 (s_w) 去更新 (s_u)

对于如何求支配点。我们把 dfs 树和半支配树的边集组成一个 DAG。这个 DAG 的支配树即原图支配树。感性理解(雾

#include<bits/stdc++.h>
#define rep(i,a,b) for(register int i=(a);i<=(b);i++)
#define per(i,a,b) for(register int i=(a);i>=(b);i--)
using namespace std;
const int N=3e5+9;

inline long long read() {
    register long long x=0, f=1; register char c=getchar();
    while(c<'0'||c>'9') {if(c=='-') f=-1; c=getchar();}
    while(c>='0'&&c<='9') {x=(x<<3)+(x<<1)+c-48,c=getchar();}
    return x*f;
}

int n,m,tick,dfn[N],did[N],anc[N],s[N],deg[N],dep[N],lc[N],f[N][29],sz[N];
vector<int>e[N],re[N],de[N],te[N];

int id[N],ms[N];
int find(int u) {
	if(u==id[u]) return u;
	int of=id[u]; id[u]=find(id[u]);
	if(dfn[s[ms[of]]]<dfn[s[ms[u]]]) ms[u]=ms[of];
	return id[u];
}

void dfs1(int u,int fa) {
	dfn[u]=++tick, did[tick]=u, anc[u]=fa;
	for(auto v:e[u]) if(!dfn[v]) dfs1(v,u);
}
void get_semi() {
	per(dt,n,2) {
		int u=did[dt],res=n; if(!u) continue;
		for(auto v:re[u]) if(dfn[v]) {
			if(dfn[v]<dfn[u]) res=min(res,dfn[v]);
			else find(v),res=min(res,dfn[s[ms[v]]]);
		}
		s[u]=did[res], id[u]=anc[u];
	}
	rep(i,2,n) de[anc[i]].push_back(i), de[s[i]].push_back(i), deg[i]=2;
}
void build(int u) {
	dep[u]=dep[lc[u]]+1, f[u][0]=lc[u];
	te[lc[u]].push_back(u);
	rep(i,1,19) f[u][i]=f[f[u][i-1]][i-1]; 
}
int lca(int u,int v) {
	if(dep[u]<dep[v]) swap(u,v);
	per(h,19,0) if(dep[f[u][h]]>=dep[v]) u=f[u][h];
	if(u==v) return u;
	per(h,19,0) if(f[u][h]!=f[v][h]) u=f[u][h],v=f[v][h];
	return f[u][0];
}
void get_dom() {
	queue<int>q; q.push(1);
	dep[1]=1;
	while(!q.empty()) {
		int u=q.front(); q.pop();
		build(u);
		for(auto v:de[u]) {
			deg[v]--;
			lc[v]=(lc[v]?lca(lc[v],u):u);
			if(!deg[v]) q.push(v);
		}
	}
}
void dfs2(int u) {
	for(auto v:te[u]) dfs2(v), sz[u]+=sz[v];
	sz[u]++;
}

int main() {
	n=read(), m=read();
	rep(i,1,m) {
		int u=read(), v=read();
		e[u].push_back(v), re[v].push_back(u);
	}
	rep(i,1,n) id[i]=ms[i]=s[i]=i;
	dfs1(1,0);
	get_semi();
	get_dom();
	dfs2(1);
	rep(i,1,n) printf("%d ",sz[i]); puts("");
	return 0;
}
原文地址:https://www.cnblogs.com/TetrisCandy/p/14629448.html