首页 > 其他分享 >支配树

支配树

时间:2022-10-04 18:55:24浏览次数:25  
标签:支配 fa int siz dep lca

对于任意两个点 \(u, v\),若从源点出发到达顶点 \(v\) 的所有路径都需要经过顶点 \(u\),则称顶点 \(u\) 支配顶点 \(v\)。特别地,每个顶点支配其自身。

对于任意一个点 \(v\),我们将图中支配顶点 \(v\) 的顶点集合称为 \(v\) 的受支配集 \(D_v\)。

支配树是这样一棵树,它的点集为原图点集,对于每个点 \(v\),它在支配树上对应的节点到根的链上所有点等于 \(D_v\)。
不难发现这样的结构肯定是存在的。

支配集的求法:删掉这个点,看看跟源点还连通的有哪些。复杂度是 \(o(n^2)\) 的。

支配树的求法
对于一般图而言,常有 \(O(n^2)\) 的求支配树的方法。即对于每个点的支配集中的点按照到源点(支配树根)的 dis 从小到大排序,对于相邻两点,若树边不存在则连接。
对于 DAG 而言,存在 \(O(n\log n)\) 的求支配树方法。无需求支配集,而只需要对所有点按照拓扑序枚举,试图实时维护已枚举到的所有点的支配树;对于当前点,枚举所有指向它的点,它们在当前支配树上的共同的 lca 就是当前点在支配树上的父亲。

【例1】[联合省选2021A]支配
本题在建立支配树之后分类讨论,假如加入的是返祖边,那么不影响,否则——
设这条边是 \(x\to y\),\(l=\text{lca}(x,y)\)。
受影响的点必然在 \(l\) 的子树中。
对于每个 \(y\) 子树中的点,考虑它在原图中的出边指向的点 \(p\),若此点在 \(x\) 的子树中,并且它不是 \(x\) 的儿子,那么它一定是满足条件的;
从满足条件的点出发可以影响到一定范围内的点,它们因满足条件的点的支配集改动而改动:从一个满足条件的点出发枚举它在原图中的出边指向的点,若此点在 \(l\) 的子树中,并且它到 \(\text{lca}(p,x)\) 的距离要 \(\ge 2\)。
可以通过画几张图得到这个做法。

#include <bits/stdc++.h>
using namespace std;
inline int read(){
	register char ch=getchar();register int x=0;
	while(ch<'0'||ch>'9')ch=getchar();
	while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	return x;
}
void print(int x){
	if(x/10)print(x/10);
	putchar(x%10+48);
}
const int N=3005;
int n,m,q,tmp,dfc,dis[N][N],ord[N],fa[N],siz[N],dep[N],dfn[N],out[N],lca[N][N];
bitset<N>in_tree,vis,zp[N],good,bk;
vector<int>G[N],vec,H[N],T[N];
queue<int>Q;
struct E{int u,v;}e[N*2];
void dfs(int x,int p){
	dep[x]=dep[p]+1,fa[x]=p,siz[x]=1,dfn[x]=++dfc;
	for(int y:T[x])if(y^p)dfs(y,x),siz[x]+=siz[y];
	out[x]=dfc;
}
int X,Y;
void dfs1(int x,int p){
	if(dfn[x]>=dfn[Y]&&dfn[x]<=out[Y]){
		good[x]=1;
		for(int y:G[x])if(dfn[y]>=dfn[X]&&dfn[y]<=out[X]&&dep[y]-1>dep[lca[y][X]])good[y]=1;
	}
	for(int y:T[x])if(y^p){
		dfs1(y,x);
	}
}
void dfs2(int x,int p){
	good[x]=1;
	for(int y:G[x])if(!good[y]&&dep[y]-1>dep[lca[y][X]]){
		dfs2(y,x);
	}
}
vector<int>rou;
void Dfs(int x,int p){
	rou.emplace_back(x);
	for(int y:rou)lca[x][y]=lca[y][x]=y;
	for(int y:T[x])if(y^p)Dfs(y,x);
	rou.pop_back();
}
int Lca(int x,int y){
	if(lca[x][y])return lca[x][y];
	return lca[x][y]=Lca(fa[x],y);
} 
int main(){
	n=read(),m=read(),q=read();
	for(int i=1;i<=m;i++){
		e[i].u=read(),e[i].v=read();
		G[e[i].u].push_back(e[i].v);
	}
	memset(dis,0x3f,sizeof dis);
	for(int st=1;st<=n;st++){
		Q.push(st),dis[st][st]=0;
		while(!Q.empty()){
			int x=Q.front();Q.pop();
			if(st==1)ord[++tmp]=x;
			for(int y:G[x])
				if(dis[st][y]>dis[st][x]+1)
					dis[st][y]=dis[st][x]+1,Q.push(y);
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++)vis[j]=0;
		if(i!=1)Q.push(1),vis[1]=1;
		while(!Q.empty()){
			int x=Q.front();Q.pop();
			for(int y:G[x])if(y!=i&&!vis[y]){
				vis[y]=1;
				Q.push(y);
			}
		}
		for(int j=1;j<=n;j++)if(!vis[j])zp[i][j]=1;
	}
	in_tree[1]=1;
	for(int i=1;i<=n;i++){
		vec.clear();
		for(int j=1;j<=n;j++)if(zp[ord[j]][i]){
			vec.emplace_back(ord[j]);
		}
		for(int j=1;j<vec.size();j++)if(!in_tree[vec[j]])
			in_tree[vec[j]]=1,T[vec[j-1]].emplace_back(vec[j]);
	}
	dfs(1,0);
	Dfs(1,0);
	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)Lca(i,j);
	for(int x,y;q--;){
		x=read(),y=read();
		int l=lca[x][y];
		if(y==l||dep[y]==dep[l]+1){puts("0");continue;}
		X=x,Y=y;
		dfs1(1,0);
		for(int i=good._Find_first();i<=n;i=good._Find_next(i))dfs2(i,0);
		int ans=0;
		for(int i=good._Find_first();i<=n;i=good._Find_next(i))ans++,good[i]=0;
		print(ans),putchar('\n');
	}
}

【例2】[ZJOI2012]灾难
按照正文中的方法做就好了。注意建立超级源点。

#include <bits/stdc++.h>
using namespace std;
const int N=65550;
int n,tot,fa[N][17],ord[N],in[N],dep[N],siz[N];
vector<int>G[N],vec[N],T[N];
queue<int>Q;
int glca(int u,int v){
	if(!u)return v;
	if(u==v)return u;
	if(dep[u]>dep[v])swap(u,v);
	for(int i=16;~i;i--)if(dep[fa[v][i]]>=dep[u])v=fa[v][i];
	if(u==v)return u;
	for(int i=16;~i;i--)if(fa[u][i]!=fa[v][i])u=fa[u][i],v=fa[v][i];
	return fa[u][0];
}
void dfs(int x,int p){
	siz[x]=1;
	for(int y:T[x])dfs(y,x),siz[x]+=siz[y];
}
int main(){
	scanf("%d",&n);
	for(int i=1,t;i<=n;i++){
		while(scanf("%d",&t),t)vec[i].emplace_back(t),G[t].emplace_back(i),in[i]++;
	}
	dep[n+1]=1;
	for(int i=1;i<=n;i++)if(!in[i])fa[i][0]=n+1,dep[i]=2,T[n+1].emplace_back(i),vec[i].emplace_back(n+1),Q.push(i);
//"vec[i].emplace_back(n+1)"很容易漏
	while(!Q.empty()){
		int x=Q.front();Q.pop();
		ord[++tot]=x;
		for(int y:G[x]){
			if(!--in[y])Q.push(y);
		}
	}
	for(int r=1;r<=n;r++){
		int i=ord[r];
		int l=0;
		for(int j:vec[i])l=glca(l,j);
		T[l].emplace_back(i),dep[i]=dep[l]+1,fa[i][0]=l;
		for(int j=1;j<=16;j++)fa[i][j]=fa[fa[i][j-1]][j-1];
	}
	dfs(n+1,0);
	for(int i=1;i<=n;i++)printf("%d\n",siz[i]-1);
}

标签:支配,fa,int,siz,dep,lca
From: https://www.cnblogs.com/impyl/p/16754223.html

相关文章