zoukankan      html  css  js  c++  java
  • 支配树学习笔记

    支配树学习笔记

    先挖个坑,以后慢慢添

    支配点是什么

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

    为什么支配关系不变

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

    • 对于某定点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]);
    }
    
  • 相关阅读:
    集群架构搭建
    THUWC2019 游记
    【集训队互测2015】未来程序·改
    [NOIP2014普及组T1]珠心算测验
    [CF912D]Fishes
    [POJ2409]Let it Bead
    golang 统计系统测试覆盖率
    tcpdump常用方法
    数学闯关引发的思考
    linux lsof常用方法
  • 原文地址:https://www.cnblogs.com/river-flows-in-you/p/10913432.html
Copyright © 2011-2022 走看看