支配树学习笔记
看机房里都会了支配树,自己也学习了一下,去年没看懂,不知道为什么现在看起来还是蛮顺利的
下面给出几个链接,会有详细的证明,我就不详细说了
证明建议看第一个,王梦迪的图比较丰富
如果你不想看证明,那么直接看下面的结论就行了
约定
支配点:如果把点 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;
}