被树支配的恐惧
前言
你敢信你有生之年还会被树支配上几次?反正我是被支配了……
支配树解决的是一个很现实的问题:假设S给T通过各种途径提供生(mo)命(fa)之源,但这些途径都有一个共性(都经过一个点),现在你想用最小的代价(摧毁尽可能少的点)抹除T,那么你应该摧毁哪些点
概念
给定一个有向图,给定起点S,终点T,在所有从S到T的路径中,如果删去某个点,则不存在一条路径能够使S到达T,则这个点被称为支配点。由支配点构成的树叫做支配树。支配树是一颗由起点S为根的树,根到树上任意一点路径经过的点都是它的支配点。
主要变量
dfn[x],表示dfs序
id[x], 表示dfs序对应的点
dfa[x], 第一遍dfs中,每个点的父节点
sdom[x], x的半支配点
idom[x], x的支配点
fa[x], 并查集(都懂)
mindfn[x],从x到其已经被搜过的祖先的dfn的最小值
半支配点的定义(tarjan tql)
性质(证明略)
1:半支配点是唯一的(但是支配点可以有多个)
2:对于任意点w,它的半支配点必定是它在dfs\ tree上的祖先
3:对于任意节点w != s, w的支配点必然是该节点的半支配点的祖先节点
4:假设 存在v,w且v是w的祖先,那么idom[w]要么是v的后代,要么是idom[v]的祖先。
求sdom的方法(伪代码)
for_each k in G with cond (dfn[k]>dfn[u] && k.canreach(u))
if(dfn[u]>dfn[v])
sdom[u]=mindfn(v);
else
sdom[u]=mindfn{sdom[k]}
求idom的方法
如果图中存在节点v,w,且v支配w,并且w的其他支配点均支配v,我们就称v是w的最近支配点,记作idom[w]=v
(仍然是伪代码)
def P = {all vertex U on route x->sdom[x]}
for_each k=min(dfn[P[i]])
if(sdom[k]==sdom[x])
idom[x]=sdom[x]
else
idom[x]=idom[z]
总体步骤
1:对这张图进行一遍dfs,求出dfn
2:根据dfn大小从大到小遍历求出sdom
3:保留dfs树上的节点和边,求出该点祖先链中sdom的最小值mindfn[v],然后这个点会成为一颗子树的根,通过带权并查集维护各个子树并连接。通过sdom求出idom
模板
https://www.luogu.org/problem/P5180
给定一张有向图,求从1号点出发,每个点能支配的点的个数
#include <bits/stdc++.h>
using namespace std;
static constexpr int maxn = 300050;
struct node {
vector<int> e[maxn];
inline void add(int u, int v) { e[u].push_back(v); }
} a, b, c, d;
int dfn[maxn], id[maxn], dfa[maxn], sdom[maxn], idom[maxn], fa[maxn],
mindfn[maxn], cnt;
void dfs(int u) {
dfn[u] = ++cnt;
id[cnt] = u;
for (int i : a.e[u]) {
if (dfn[i]) {
continue;
}
dfa[i] = u;
dfs(i);
}
}
int find(int x) {
if (x == fa[x]) {
return x;
}
int tmp = find(fa[x]);
if (dfn[sdom[mindfn[fa[x]]]] < dfn[sdom[mindfn[x]]]) {
mindfn[x] = mindfn[fa[x]];
}
return fa[x] = tmp;
}
void zp() {
for (int i = cnt; i > 1; i--) {
int u = id[i];
for (int v : b.e[u]) {
if (!dfn[v]) {
continue;
}
find(v);
if (dfn[sdom[mindfn[v]]] < dfn[sdom[u]]) {
sdom[u] = sdom[mindfn[v]];
}
}
c.add(sdom[u], u);
fa[u] = dfa[u];
u = fa[u];
for (int v : c.e[u]) {
find(v);
sdom[mindfn[v]] == u ? idom[v] = u : idom[v] = mindfn[v];
}
}
for (int i = 2; i <= cnt; i++) {
int u = id[i];
if (idom[u] != sdom[u]) {
idom[u] = idom[idom[u]];
}
}
}
void zp_write(int n) {
for (int i = 2; i <= n; i++) {
d.add(idom[i], i);
}
}
int ans[maxn];
void dfs2(int u) {
ans[u] = 1;
for (int i : d.e[u]) {
dfs2(i);
ans[u] += ans[i];
}
}
int main() {
int n, m;
cin >> n >> m;
int u, v;
for (int i = 1; i <= m; i++) {
cin >> u >> v;
a.add(u, v);
b.add(v, u);
}
for (int i = 1; i <= n; i++) {
sdom[i] = fa[i] = mindfn[i] = i;
}
dfs(1);
zp();
zp_write(n);
dfs2(1);
for (int i = 1; i <= n; i++) {
cout << ans[i] << " ";
}
cout << endl;
return 0;
}