支配树学习日志

支配树 学习日志

给定一张有向图 $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}$ 算法中的关键一步。

后记

  1. 上述式子的证明还需阅读并研究。
  2. 这种带权并查集不但可以路径压缩,而且还可以通过一定技巧按秩合并,将时间复杂度降为 $O\big(m \alpha(n)\big)$。

原文地址:https://www.cnblogs.com/dev-cpp/p/10124422.html

时间: 2024-10-09 14:16:33

支配树学习日志的相关文章

支配树学习笔记

支配树(dominator tree) 学习笔记 学习背景 本来本蒟蒻都不知道有一个东西叫支配树……pkuwc前查某位的水表看见它的大名,甚感恐慌啊.不过好在pkuwc5道题(嗯?)都是概率期望计数,也不知是好还是不好,我在这些方面也只是不好不差……扯远了. 考挂之后也没什么心思干别的,想起支配树这个东西,于是打算学一下. 技能介绍(雾) 支配树是什么?不如直接讲支配树的性质,从性质分析它的定义. 先大概讲一下它是来求什么的. 问题:我们有一个有向图(可以有环),定下了一个节点为起点s.现在我们

康复计划#4 快速构造支配树的Lengauer-Tarjan算法

本篇口胡写给我自己这样的老是证错东西的口胡选手 以及那些想学支配树,又不想啃论文原文的人- 大概会讲的东西是求支配树时需要用到的一些性质,以及构造支配树的算法实现- 最后讲一下把只有路径压缩的并查集卡到$O(m \log n)$上界的办法作为小彩蛋- 1.基本介绍 支配树 DominatorTree 对于一个流程图(单源有向图)上的每个点$w$,都存在点$d$满足去掉$d$之后起点无法到达$w$,我们称作$d$支配$w$,$d$是$w$的一个支配点. 支配$w$的点可以有多个,但是至少会有一个.

【C语言】【数据结构】菜鸟学习日志(四) 用二叉树实现非递归排序

唉,由于要备战考研,这篇博文可能是我这一年最后一次更新啦! 其实断断续续的也没有写很多,而且大多都是很初级.很简单的东西,没有和大家分享什么高阶的东西.这也正应了我们<菜鸟学习日志>的标题嘛! 不过说回来我还是很喜欢写博文的.一方面总结学到的知识,以后也可以自己看看别做了就忘了:另一方面,写博文也让我在学习的过程中更加认真,以免分享了错误的知识. 写的东西好不好呢是一说,好像有一些点击量,不过看的人估计也不多.只是我还算乐在其中吧! 大学生活说到底过得有点浪了,导致我苦逼地走向了考研的不归路-

Cocos2d-x 3.1.1 学习日志13--物理引擎登峰造极之路

cocos2dx在设计之初就集成了两套物理引擎,它们是box2d和chipmunk.我目前使用的是最新版的cocos2dx 3.1.1.引擎中默认使用的是chipmunk,如果想要改使用box2d的话,需要修改对应的android工程或者是ios工程的配置文件. 在2.x版本的cocos中,使用物理引擎的步骤十分繁琐.但在3.x版本中变得非常方便了.我这次的学习目标是制作一个打砖块的小游戏. 首先,现在的Scene类提供了一个静态工厂方法,用以创造一个集成物理引擎的场景. Scene::init

winform学习日志(二十三)---------------socket(TCP)发送文件

一:由于在上一个随笔的基础之上拓展的所以直接上代码,客户端: using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Net.Sockets; using Sys

Trie树学习2

数组实现的Trie树 字符容量有限,可以使用链表实现更为大容量的Trie #include <iostream> #include <cstdio> #include <string> #include <cstring> #include <vector> #include <map> #include <set> #include <algorithm> #include <cstdlib> #

Linux学习日志2-vim使用基础

vim是linux操作系统下的一个文本编辑工具,功能非常强大,但刚学习起来比较复杂.vim的所有功能要讲明白得有几百页,在这里只是记录一下vim的一些基本用法. 首先vim打开文件的三种方式: vim +# xxx(#是数字):表示打开xxx文件并将光标定位到指定行. vim -o xx1 xx2 xx3:表示同时打开三个文件,垂直分割显示 vim -O xx1 xx2 xx3:表示同时打开三个文件,水平分割显示 多个文件间跳转:键入ctrl+w后:→向左.←向右.↑向上.↓向下 vim打开文件

Linux学习日志day1——无人值守系统安装DHCP+TFTP+PXE+Kickstar

Linux学习日志day1--无人值守批量系统远程网络安装(DHCP+TFTP+PXE+Kickstar)                                         --作者:江信瀚 服务器环境介绍: 主机名:workstation.example.com 关闭SElinux以及防火墙 虚拟机:VMware(关闭了VMware的DHCP服务) 网卡配置: 静态IP获取! IPV6全部都删除,因为根本用不到 子网IP可以在VMware中设置 8.8.8.8是谷歌的DNS服务器

Cocos2d-x 3.1.1 学习日志4--cocos2d-x解决中文乱码问题的几种办法

做个打飞机的游戏,由于版本太新,网上基本没有教教程,我的版本是cocos2d-x 3.1.1的,今天遇到cocos2dx中中文乱码的问题.无奈只好Google百度寻求答案,明白了这个问题的缘由.因为cocos2d-x内部是以utf8处理文本的,而VS直接输入时文本编码为GBK,如果添加L标志,则为Unicode编码. 解决这个问题有三种办法: 将源代码文件保存为utf8编码,不过由于编译器的问题,这种方式会导致很多无法预测的问题 将字符串用utf8编码集中存到一文件中,然后用代码读取这些字符串来