• 支配树学习笔记


    支配树学习笔记

    看机房里都会了支配树,自己也学习了一下,去年没看懂,不知道为什么现在看起来还是蛮顺利的

    下面给出几个链接,会有详细的证明,我就不详细说了

    meowww

    tarjan论文翻译

    王梦迪的营员交流

    证明建议看第一个,王梦迪的图比较丰富

    如果你不想看证明,那么直接看下面的结论就行了

    约定

    支配点:如果把点 y 去掉,那么从源点将无法到达点 x,那么称 y 是 x 的支配点

    最近支配点:点 x 的支配点有多个,除去平凡支配点(自己和源点),找到点 y 满足 y 是 x 的支配点且 x 的其他非平凡支配点都支配 y,称 y 为 x 的最近支配点,记作 (idom(x)=y)

    将所有最近支配点向 x 连边形成的树,我们称之为支配树

    我们现在将有向图 dfs 一遍并按照遍历顺序重新编号

    半支配点:编号最小的 v 且存在一条路径 v -> x 满足除了 v 和 x,剩下的路径上的点编号都大于 x,记作 (sdom(x)=v)

    重要结论

    (sdom)(idom)

    (u) 为满足 (u)(sdom(w) o w) 上不同于 (sdom(w)) 的一点,有

    [large idom(w)=left{egin{matrix} sdom(w) &&(sdom(u)=sdom(w))\ idom(u) &&(sdom(u)<sdom(w)) end{matrix} ight. ]

    (sdom)

    第一种情况,(sdom(w) = v),要求有边直接从 (v) 连到 (w)(v < w)

    第二种情况,(sdom(w) = sdom(u)(u > w)),要求存在点 (v) 有边从 (v)(w)

    具体实现

    这里只说一点(可能只有我有问题)就是不能用自己更新 idom 而用父亲更新的原因是自己的值不能影响到下面的后代,而用父亲是父亲的 sdom 还是本身所以没事

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<vector>
    using namespace std;
    int read(void) {
    	int x = 0; char c = getchar();
    	while (!isdigit(c)) c = getchar();
    	for (;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+(c^48);
    	return x;
    }
    
    const int N = 2050000;
    int n, m, tot, cnt;
    int fa[N], dfn[N], id[N];
    int val[N];
    int h[N], ne[N<<1], to[N<<1];
    int f[N], sdom[N], pre[N];
    
    void add(int *h,int x,int y) {
    	to[++tot] = y;
    	ne[tot] = h[x], h[x] = tot;
    }
    
     
    void dfs(int x,int fr) {
    	dfn[x] = ++cnt, id[cnt] = x;
    	fa[x] = fr;
    	for (int i = h[x]; i; i = ne[i]) 
    		if (!dfn[to[i]]) dfs(to[i], x);
    }
    
    int find(int x) {
    	if (x == f[x]) return x;
    	int rt = find(f[x]);
    	if (dfn[sdom[val[f[x]]]] < dfn[sdom[val[x]]]) 
    	val[x] = val[f[x]];
    	return f[x] = rt;
    }
    int lat[N], idom[N], ans[N], cdy[N];
    
    void put(int *a,int n) {
    	for (int i = 1;i <= n; i++) 
    		cout << a[i] << ' ';
    	cout << endl;
    }
    
    void tarjan(void) {
    	for (int i = cnt;i >= 2; i--) {
    		int x = id[i];
    		for (int j = pre[x]; j; j = ne[j]) {
    			int y = to[j];
    			if (!dfn[y]) continue;
    			find(y);
    			if (dfn[sdom[val[y]]] < dfn[sdom[x]])
    				sdom[x] = sdom[val[y]];
    		}
    		add(lat, sdom[x], x);
    		f[x] = fa[x], x = fa[x];
    		for (int j = lat[x]; j; j = ne[j]) {
    			int y = to[j]; find(y);
    			if (sdom[val[y]] == x) idom[y] = x;
    			else idom[y] = val[y];
    		}
    		lat[x] = 0;
    	}
    	for (int i = 2;i <= cnt; i++) {
    		int x = id[i];
    		if (idom[x] != sdom[x]) idom[x] = idom[idom[x]];
    	}
    }
    
    void get_ans(int x) {
    	ans[x] = 1;
    	for (int i = cdy[x]; i; i = ne[i]) {
    		int y = to[i];
    		get_ans(y); ans[x] += ans[y];
    	}
    }
    
    int main() {
    	n = read(), m = read();
    	for (int i = 1;i <= m; i++) {
    		int x = read(), y = read();
    		add(h, x, y), add(pre, y, x);
    	}
    	for (int i = 1;i <= n; i++) 
    		sdom[i] = f[i] = val[i] = i;
    	dfs(1, 0); tarjan();
    	tot = 0;
    	for (int i = 2;i <= n; i++) 
    		if (idom[i]) add(cdy, idom[i], i);
    	get_ans(1);
    	for (int i = 1;i <= n; i++) printf ("%d ", ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    编写通用shell脚本启动java项目,适用于多数服务,只需修改服务名即可
    linux操作系统安装jdk
    搭建dubbo+zookeeper+dubboadmin分布式服务框架(windows平台下)
    二.传统服务的拆分策略
    一.把传统服务做成dubbo分布式服务架构的步骤
    Android IOS WebRTC 音视频开发总结(六)-- iOS开发之含泪经验
    Android WebRTC 音视频开发总结(五)-- webrtc开发原型
    Android WebRTC 音视频开发总结(四)-- webrtc传输模块
    Android WebRTC 音视频开发总结(三)-- 信令服务和媒体服务
    Android WebRTC 音视频开发总结(二)-- webrtcdemo介绍
  • 原文地址:https://www.cnblogs.com/Hs-black/p/13442519.html
Copyright © 2020-2023  润新知