支配树学习笔记
在我的博客获得不一定更好的体验。
约定:本文中用
支配关系与支配点
对于一张有向图和一个起始点
方便起见,我们认为
若
引理 1:支配关系是偏序关系。
根据定义有自反性,反对称性和传递性也是好证的。
引理 2:若
u \mathbin{\mathrm{dom}} w, v \mathbin{\mathrm{dom}} w ,则u \mathbin{\mathrm{dom}} v 或v \mathbin{\mathrm{dom}} u 。
使用反证法,若不满足的话一定不同时满足
支配树
根据上面两个引理,我们可以发现点集和支配关系构成的偏序集的哈斯图构成了一棵以
注:一个偏序集
对于除
根据定义可以得到,每个点的支配点是其在支配树上的所有祖先(包括其本身),每个点支配其子树中的所有点。
外向树的支配树
外向树的支配树即为其本身。
DAG 的支配树
引理 3:
\forall u, v, u \ne v, u \mathbin{\mathrm{dom}} v \iff \forall w, (w, v) \in E, u \mathbin{\mathrm{dom}} w
证明略。
对于 DAG,我们考虑按拓扑序构造支配树。
对于一个点
Lengauer–Tarjan 算法
Lengauer–Tarjan 算法用于在
首先对原图进行 dfs 求出 dfs 序以及 dfs 生成树
以下定义
半支配点
我们定义一个点
注:在某些地方也称存在一条上述路径的点为半支配点,称同时满足最小的点为最小半支配点。
约定:
以下「满足半支配点的条件」指存在一条上述路径但不一定最小。
以下
以下讨论中默认点不包括
引理 4:对于任意点
u ,sdom(u) < u
证明考虑
引理 5:对于任意点
u ,idom(u) 一定是u 的祖先。
证明考虑
引理 6:对于任意点
u ,sdom(u) 一定是u 的祖先。
证明考虑从
引理 7:对于任意节点
u ,idom(u) 一定是sdom(u) 的祖先(或就是sdom(u) )。
证明考虑
引理 8:对于任意点
u, v ,u 是v 的祖先,要么u 是idom(v) 的祖先(或相等),要么idom(v) 是idom(u) 的祖先(或相等)。
对于树上任意
定理 1:
sdom(u) = \min(\{v \mid (v, u) \in E, v < u\} \cup \{sdom(w) \mid w > u \land \exists v > u, (v, u) \in E, \text{s.t. } w\text{ 是 }v\text{ 的祖先或 }w = v\})
证明:设右式的值为
先证
若
再证
综上,
根据定理 1,我们可以得到一个
求支配点
方法一:
在
证明:显然我们只需要考虑有祖先关系的点。首先容易发现原图存在的支配关系依然存在,然后要证明原图不存在的支配关系在新图上也不存在。
对于一个点
其实这个做法很好写,因为每个点只会有两个入度而且在
方法二:
但是有人认为上面这个做法太不优雅了,所以这里给出另一种做法。
定理 2:对于任意节点
u ,设v 是sdom(u) 到u 这条链上sdom 最小的点(不包括u 和sdom(u) ),若sdom(v) \ge sdom(u) ,则idom(u) = sdom(u) 。
证明:等价于证
对于
假设存在一条到
我们把树切成五部分,分别是
考虑
我们找到最小的
定理 3:对于任意节点
u ,设v 是sdom(u) 到u 这条链上sdom 最小的点(不包括u 和sdom(u) ),若sdom(v) < sdom(u) ,则idom(u) = idom(v) 。
证明:因为
类似地,假设存在一条到
同样的分析方法可以得到
可以发现这条路径上
此时我们发现存在一条路径
根据上面两个定理,我们可以得到:
在求
参考代码
int n, m; // n, m 为点数和边数
struct graph {
int tot, hd[200005];
int nxt[300005], to[300005];
void add(int u, int v) {
nxt[++tot] = hd[u];
hd[u] = tot;
to[tot] = v;
return ;
}
} g, fg; // g 为原图,fg 为反图
int timer, fa[200005], dfn[200005], id[200005];
int sdom[200005], idom[200005];
struct dsu {
int fa[200005], mn[200005];
dsu() {for (int i = 1; i < 200005; i++) fa[i] = mn[i] = i;}
int find(int x) {
if (x == fa[x]) return x;
int tmp = find(fa[x]);
if (dfn[sdom[mn[fa[x]]]] < dfn[sdom[mn[x]]]) mn[x] = mn[fa[x]];
return fa[x] = tmp;
}
} d;
vector<int> vec[200005];
void dfs(int now) {
id[dfn[now] = ++timer] = now;
for (int i = g.hd[now]; i; i = g.nxt[i])
if (!dfn[g.to[i]]) fa[g.to[i]] = now, dfs(g.to[i]);
return ;
}
void solve() {
dfs(1);
for (int i = 1; i <= n; i++) sdom[i] = i;
for (int i = timer; i >= 1; i--) {
int u = id[i];
for (int v : vec[u]) {
d.find(v);
if (sdom[d.mn[v]] == u) idom[v] = u;
else idom[v] = d.mn[v]; // 这里是一个省数组的写法,将 idom 和并查集用同一个数组维护了。
}
if (i == 1) continue;
for (int j = fg.hd[u]; j; j = fg.nxt[j]) {
if (!dfn[fg.to[j]]) continue;
if (dfn[fg.to[j]] < dfn[sdom[u]]) sdom[u] = fg.to[j];
else if (dfn[fg.to[j]] > dfn[u]) {
d.find(fg.to[j]);
if (dfn[sdom[d.mn[fg.to[j]]]] < dfn[sdom[u]]) sdom[u] = sdom[d.mn[fg.to[j]]];
}
}
vec[sdom[u]].push_back(u);
d.fa[u] = fa[u];
}
for (int i = 2; i <= timer; i++)
if (idom[id[i]] != sdom[id[i]]) idom[id[i]] = idom[idom[id[i]]];
return ;
}
例题
- P5180 【模板】支配树
- P2597 [ZJOI2012] 灾难
- P7520 [省选联考 2021 A 卷] 支配
- CF757F Team Rocket Rises Again
参考资料
- https://www.luogu.com.cn/blog/zhw-ifmt/z-zuo-p-pian-s-shu-post
- https://www.bilibili.com/video/BV1DA411k79m/