支配树 学习日志
给定一张有向图 $G=(V, E)$,其中 $\lvert V \rvert=n, \lvert E \rvert=m$,以及根 $r \in V$。
我们称顶点 $x\ (x \ne r)$ 可达,当且仅当存在一条从 $r$ 到 $x$ 的路径。
对于 $x \ne r$ 且可达的 $x$,如果 $y \ne x$,且删去 $y$ 后 $x$ 不可达,那么就说 $y$ 支配 $x$。特别地,$r$ 一定支配 $x$。
不可达的点的支配点没有定义,因此我们不妨设 $G$ 的所有顶点都可达。
我们以 $r$ 为根,进行一次深度优先遍历 $\newcommand{dfs}{\mbox{dfs}}(\dfs)$。那么,$x$ 的支配点一定都在 $\dfs$ 树上从 $r$ 到 $x$ 的路径上,也就是 $x$ 的支配点一定是 $\dfs$ 树上 $x$ 的祖先。
我们定义 $x$ 的直接支配点 $\mbox{idom}(x)$ 是 $\dfs$ 树上深度最大的支配 $x$ 的顶点。
我们构造一张新的有向图 $T=(V,E_T)$,其中 $E_T=\big\{\langle \mbox{idom}(x), x\rangle \big\vert x \in V\setminus\{r\}\big\}$。根据上面的结论,我们知道这张有向图是一棵以 $r$ 为根的树,称为支配树。
不难发现,一个点在支配树上的祖先集合就是其支配点集合。
求解 DAG 的支配树
按照拓扑序加入结点。
定义 $\newcommand{lca}{\mbox{LCA}}\lca S$ 表示:当前所有已经加入的结点的支配树上,顶点集合 $S$ 的所有元素的最近公共祖先。
$$\mbox{idom}(x)=\begin{cases}r&\mbox{if }\langle r, x \rangle \in E;\\\lca\{\mbox{idom}(y)\mid\langle y, x \rangle \in E\}&\mbox{if }\langle r, x \rangle \notin E.\end{cases}$$
按照上述式子递推,用倍增求 $\lca$,时间复杂度为 $O(m\log n)$。
求解支配树的 $\mbox{Lengauer-Tarjan}$ 算法
设 $\mbox{fa}_x$ 表示顶点 $x$ 在 $\dfs$ 树上的祖先 $(x \ne r)$,$\mbox{sub}_x$ 表示顶点 $x$ 在 $\dfs$ 树上的子树顶点集(特殊地,$x \in \mbox{sub}_x$)。
记顶点 $u < v$,当且仅当 $u$ 在 $v$ 之前被遍历到。
记 $\newcommand{anc}{\dashrightarrow}u \anc v$ 表示 $u$ 是 $v$ 在 $\dfs$ 树上的祖先。对于 $u \anc v$,记 $[u, v]$ 表示在 $\dfs$ 树上 $u$ 到 $v$ 的唯一简单路径,$(u, v]$ 表示不含 $u$ 的树上 $u$ 到 $v$ 的简单路径。
如果顶点 $x, y\ (x \ne r)$ 满足 $y\anc x$,且存在一条路径 $y \to v_1 \to v_2 \to \cdots \to v_k \to x$ 使得 $v_1, v_2, \ldots, v_k>x$,就说 $y$ 半支配 $x$ 。
我们定义 $x$ 的半支配点 $\mbox{semi}(x)$ 是 $\dfs$ 树上深度最小的半支配 $x$ 的顶点。
我们给出求解半支配点的公式:
$$\mbox{semi}(x)=\min\big(\{u\mid\langle u,x \rangle\in E\}\cup\{\mbox{semi}(v)\mid v > x, \exists w \in V, \langle w,x \rangle \in E \wedge v \anc w\}\big)$$
根据半支配点,我们再给出计算 $\mbox{idom}(x)$ 的公式:
设 $u$ 是路径 $(\mbox{semi}(x), x]$ 上 $\mbox{semi}(u)$ 最小的点。
$$\mbox{idom}(x)=\begin{cases}\mbox{semi}(x)&\mbox{if }\mbox{semi}(x)=\mbox{semi}(u);&(1)\\\mbox{idom}(u)&\mbox{if }\mbox{semi}(x) \ne \mbox{semi}(u).&(2)\end{cases}$$
我们在计算 $\mbox{semi}(x)$ 的同时计算能够直接得出的 $\mbox{idom}(x)$,或者记录 $u_x$ 满足 $\mbox{idom}(x)=\mbox{idom}(u_x)$。
我们最后按照 $\dfs$ 序递推 $(2)$ 式,建出支配树。
算法实现上,为了查找 $\dfs$ 树上祖先后代路径 $(\mbox{semi}(x), x]$ 上 $\mbox{semi}(u)$ 最小的 $u$,我们可以按照逆 $\dfs$ 序加入点,利用带权并查集维护。我们计算直接支配点的过程中,我们用链表等数据结构维护一个集合 $B_y(x)=\{u \in \mbox{sub}_y \mid \mbox{semi}(u)=x\}$,其中 $x=\mbox{fa}_y$。当顶点 $x$ 的子树处理完毕时,结算 $B_y(\mbox{fa}_x)$ 的所有元素的直接支配点。实现时,由于这些 $B_y(x)$ 的生存周期互不相交,我们可以只维护一个 $B(x)$ 表示当前的 $B_y(x)$,结算后清空 $B(x)$ 转到下一棵子树即可。该算法时间复杂度为 $O(m \log n)$。
通过解决支配树问题获得的一些启发
$\lca$ 的 $\mbox{Tarjan}$ 算法中利用了并查集来维护一类离线查询路径信息的问题,这种做法依然是支配树的 $\mbox{Lengauer-Tarjan}$ 算法中的关键一步。
后记
- 上述式子的证明还需阅读并研究。
- 这种带权并查集不但可以路径压缩,而且还可以通过一定技巧按秩合并,将时间复杂度降为 $O\big(m \alpha(n)\big)$。
原文地址:https://www.cnblogs.com/dev-cpp/p/10124422.html