zoukankan      html  css  js  c++  java
  • 支配树

    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;
    }
    
  • 相关阅读:
    指令到底是什么?机器码又是什么?
    汇编基础最后一篇--机器语言指令
    剑指OFFER----面试题34. 二叉树中和为某一值的路径
    剑指OFFER----面试题33. 二叉搜索树的后序遍历序列
    剑指OFFER----面试题32
    剑指OFFER----面试题31. 栈的压入、弹出序列
    剑指OFFER----面试题30. 包含min函数的栈
    剑指OFFER----面试题29. 顺时针打印矩阵
    剑指OFFER----面试题28. 对称的二叉树
    剑指OFFER----面试题27. 二叉树的镜像
  • 原文地址:https://www.cnblogs.com/TetrisCandy/p/14629448.html
Copyright © 2011-2022 走看看