支配树学习笔记

支配树学习笔记

先挖个坑,以后慢慢添

支配点是什么

从起点开始到另一个点,绕不开的点就是支配点

为什么支配关系不变

命题:为什么删除非树边加上半支配点到该点的边支配关系不变

  • 对于某定点x而言,要么是支配点,要么不是,无中间态,所以我们可以考虑反面
  • 只有x的祖先才有资格做支配点
  • 考虑某个备选y,如果它可以被略过(可以从y的某祖先w到y下x上的点z而不经过y),那么y淘汰
  • 这个w至z的路径哪部分可以淘汰人?非树边的部分。
  • 对这条路径掐头去尾,去掉树边
  • 然后我们就可以发现,w至z的路径中都是dfn大于z的点了,w就可以做z的半支配点备选了
  • 由于半支配点是取最上面的那个,所以即使w不是z的半支配点,w至z中的任意点都会被淘汰
  • 所以该淘汰的都会被淘汰
  • 那为什么未被淘汰的点的都是支配点的呢?
  • 很简单,未被淘汰的点都没有办法从上面绕到下面而不经过他,所以都是支配点

代码

#include<bits/stdc++.h>
#define now v[i]
using namespace std;
const int sz=3e5+527;
int T;
int n,m;
int u,v;
int c[sz];
int fa[sz];//dfs树上的父亲 
int f[sz],mi[sz];//f为并查集上的父亲,mi保存了并查集中semi的dfn最小的点 
int rnk[sz],dfn[sz];//排名和时间戳 
int semi[sz],idom[sz];//半支配点和支配点 
struct chart{
	int cnt;
	int v[sz],nxt[sz];
	int head[sz];
	void add(int u,int vv){
		cnt++;
		v[cnt]=vv;
		nxt[cnt]=head[u];
		head[u]=cnt;
	}
}g,fg,ng,tr;
//g为原图,fg为反图,ng为semi的图,tr为支配树 
int find(int x){
	if(f[x]==x) return x;
	int tmp=f[x];
	f[x]=find(f[x]);
	if(dfn[semi[mi[tmp]]]<dfn[semi[mi[x]]]) mi[x]=mi[tmp];
	return f[x];
}//并查集 
void dfs(int x){
	rnk[dfn[x]=++T]=x;
	for(int i=g.head[x];i;i=g.nxt[i])
		if(!dfn[g.now]) fa[g.now]=x,dfs(g.now);
}//建出dfs树 
void work(){
	for(int i=n;i>=2;i--){
		//从dfn最大的点开始 
		u=rnk[i];
		int tmp=n;
		for(int j=fg.head[u];j;j=fg.nxt[j]){
			v=fg.v[j];
			if(dfn[v]<i) tmp=min(tmp,dfn[v]);//dfn比自己小的只能做起点 
			else if(dfn[v]>i){
				find(v);
				tmp=min(tmp,dfn[semi[mi[v]]]);
			}
		}
		semi[u]=rnk[tmp];
		f[u]=fa[u];
		ng.add(semi[u],u);
		
		u=rnk[i-1];//因为rnk[i]已经和父亲连边了+需要rnk[1] 
		for(int j=ng.head[u];j;j=ng.nxt[j]) {
			v=ng.v[j];
			find(v);
			if(semi[mi[v]]==semi[v]) idom[v]=semi[v];//如果支配点等于被支配点 
			else idom[v]=mi[v];//否则记录u的支配点等于谁的支配点 
		}
	}
	for(int i=2;i<=n;++i) {
		u=rnk[i];
		if(idom[u]!=semi[u]) idom[u]=idom[idom[u]];
		tr.add(idom[u],u);
	}
	for(int i=n;i>=1;i--){
		u=rnk[i];
		c[u]=1;
		for(int i=tr.head[u];i;i=tr.nxt[i]){
			v=tr.v[i];
			c[u]+=c[v];
		}
	}
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		scanf("%d%d",&u,&v);
		g.add(u,v);
		fg.add(v,u);
	}
	for(int i=1;i<=n;i++) semi[i]=mi[i]=f[i]=i;
	dfs(1);
	work();
	for(int i=1;i<=n;i++) printf("%d ",c[i]);
}
原文地址:https://www.cnblogs.com/river-flows-in-you/p/10913432.html