支配树 瞎学笔记

支配树上支配集,支配树下我和你。(骗人的,支配树跟支配集毛关系没有)

支配树是用来求解有向图必经点问题的算法。

(u1s1 算法的证明根本不体现支配树的本质,学了也是白学,我是强迫症犯了才补上证明的,不要学我)

基础概念

对于一个有向图,选定 1​​​ 为起点(下文都假定 1 能到所有点,无自环,不失一般性),对节点 x,yy 支配 x 当且仅当 y1x 的必经点,称为 x 的支配点。不难证明支配关系的自反性、反对称性、传递性。
支配关系还有个类似传递性的神奇性质:若 xy 都支配 z,则 x,y 必有支配关系。若不然,任找一条 1z 简单路径,由支配性知 x,y 都在上面:若 x1 更近,由于 x 不支配 y,所以可以从 1 绕过 xy 再到 z,与 x 支配 z 矛盾;y 更近的情况类似。那么易证(读者自证不难):任取一条 1x 简单路径,上面所有 x 的支配点按离 1 的距离从近到远排列依次为 1=x0x1xs=x,则对任意 i[0,s] 知道 xi 的所有支配点是 x0i​​。

x 的所有支配点当中除了自己离自己最近的那个为 x 的最近支配点,记作 idomxidom1 DNE。将所有点 x1idomx 连一条边,得到一棵以 1 为根的内向树。若不然,必定存在大小大于 1 的环,由传递性可知存在 x 支配 idomx,与支配关系的反对称性矛盾。这棵树就是原图的支配树。显然,x 的全部支配点就是它在支配树上的全部祖先,并且排列顺序与在原图任意一条 1x 简单路径中离 1 的距离从近到远的顺序一致。

支配树的求法

显然,若对每个点 x1​​​ 都知道了 idomx,那么支配树自然就求出来了。

外向树

显然,以 1 为根的外向树的支配树就是自己的反图。

DAG

将 DAG 拓扑排序,那么显然,加入一个点对拓扑序在它之前的点的导出子图的支配树并没有影响。所以考虑按拓扑序依次加入,每次只要考虑当前点的最近支配点是谁,插到树上就可以了。

加入点 x​ 的时候,考虑所有边 (y,x)​,由于是按照拓扑序,所有 y​ 在之前已经考虑过了。设 z​ 是所有 y​ 在支配树上的 LCA,易证有且仅有 z​ 的所有支配点是 x​ 的所有支配点,即 idomx=z​。

考虑实现。需要实时维护 LCA,考虑树上倍增,每次接一个叶子,确实是可以动态维护倍增数组的,而且很好写,复杂度线对。贴个 P2597 的板子吧。

一般图的 tarjan 算法

一般有向图也是有线对复杂度的求支配树的算法的,叫做 tarjan 算法(Tarjan 老爷子真是 nb,总能发明很难证的算法,而且他自己还总是会证)。虽然 DAG 的特殊求法复杂度跟 tarjan 算法复杂度一样,但是好写的很多,所以 DAG 一般还是写特殊求法。

引入半支配点

找到原图从 1 出发的一棵 dfs 树(Tarjan 老爷子好像很喜欢 dfs 树耶),不失一般性地,下文认为点 i 的 dfs 序就是 i​。对 y<x,若存在一条 yx 路径满足除端点外全程大于 x,则称 yx​​ 的半支配点。

引理:对 x<y​,xy​ 的任意一条路径都必定经过它们在 dfs 树上的某个公共祖先。证明:设 z=LCA(x,y)​,则把 z​ 的祖先全删除之后 x,y​ 所在子树就裂开了,前向边和后向边对连通性屁帮助没有,而横叉边只能是大号点到小号点,所以此时 x​ 是不能到达 y​ 的。
跟据引理,不是 x​​​​ 的祖先的节点 y​ 一定不是 x​​ 的半支配点,因为 yx​ 一定经过小于 x​ 的 x​ 的祖先(由于 y<x​,所以 y​ 不可能是 x​ 的后代)。

x​ 的所有支配点也一定是 x​ 在 dfs 树上的祖先,若不然,删去之后显然可以沿着 dfs 树从 1​ 走到 x​。
x​ 所有支配点都是半支配点的祖先。证明:若存在半支配点 y​ 是支配点 z​ 的祖先,则可以先沿 dfs 树 1y​,然后从 y​ 走一条全程大于 x​ 的路径到 x​,自然地绕过了 z​,与支配性矛盾。
x​ 的 dfs 序最小的半支配点为最远半支配点,记作 sdomx​,亦即在 dfs 树上离 1​ 最近的半支配点。我们有 idomxsdomx​,知道最远半支配点是对最近支配点的一个良好近似,对求最近支配点有帮助。

最远半支配点的求法

对点 x1​,枚举所有半支配点到它的路径的一切可能的前驱 (y,x)

  • y<x 则直接用 y 更新 sdomx。正确性显然,充分性的话,由于 y<x,所以 y 只能是半支配点到 x 的路径的端点。
  • y>x​,设 w=LCA(x,y)​,则用树上路径 wy​(掐头留尾)中所有 z​ 的 sdomz​ 来更新 sdomx​。证明:首先 x=w​ 的情况不证自证,下面考虑 xw​ 的情况。首先想直接用 sdomy​ 更新,但它有可能走区间 (x,y)​ 之间的点 u​ 啊,这就没考虑到。若 LCA(u,y)=w​,则跟据引理,由 u<y​ 知道一旦走到了 u​,就一定要经过 w​ 的祖先才能到 y​,这是一定小于 x​ 的,所以 x​ 的半支配点到 y​ 的路径上小于 y​ 的只能是 wy​(掐头留尾)中所有的 z​。设从 x​ 半支配点走到 y​ 的路径上第一次交到这条直链上交的是 z0​,由于是第一次交上,显然之前走的都大于 y​,那自然也大于 z0​,交上去之后可以直接沿着 dfs 树走到 y​,最后走到 x​。所以这个用所有 sdomz​​ 的更新方案是既正确又充分的。​​​

通过最远半支配点求最近支配点

对点 x1​​​​​,我们设路径 p=1sdomx​​(留头去尾),q=sdomxx​​(掐头留尾),y​​ 是 q​​ 中最远半支配点最远(亦即 dfs 序最小)的点,显然有 sdomysdomx​​​​​。分成两种情况:

  • sdomy=sdomx​​,则 idomx=sdomx​​。证明:如果 sdomx=1​​ 那根据 idomxsdomx​​ 就显然了,下面考虑 sdomx1​​ 的情况。此时显然对任意 i,j​​,pi​​ 不半支配 qj​​。假设存在 1x​​ 的不经过 sdomx​​​ 的简单路径,由于 1​​ 不半支配 x​​,一定经过小于 x​​ 的点 z​​。而由于 z<x​​,跟据引理 zx​​ 必经过 fax​​ 的祖先。如果祖先为 qi​​,那就对 1qi​​ 重新使用上述分析一直到祖先为 pi​​ 为止(一定可实现,因为不能经过 sdomx​​​ 哦)。然后又可以对 pix​​ 重新施加上述分析得到一个新的 pj​​,如此往复得到 pk,po,。注意到我们考虑的是简单路径,p 里所有点迟早都会被得到一遍,就不能继续了,而我们的分析是可以一直持续下去的,矛盾。至此我们知道了 sdomxx 的支配点之一,即 sdomxidomx,结合 idomxsdomx​​,得证。
  • sdomy<sdomx​,则 idomx=idomy​​​。证明:显然有 sdomx<y,由 idomxsdomxidomx<y。而若 idomx>idomy,此时 idomx 一定不支配 y(否则 idomy 应该调整到 idomx 这么近),那么有避开 idomx 的路径 1y,再沿 dfs 树走到 x,与 idomx 支配 x 矛盾。所以一定有 idomxidomy,只要证 idomy 支配 x 则有 idomx=idomy​。
    类似上面,假设存在 1x 的不经过 idomy 的简单路径。既然不经过 idomy 了,那么直链 idomyy 上面的点都不能经过了,因为一旦交上去便可沿 dfs 树走到 y,绕开了 idomy,与支配性矛盾。由于 idomysdomy,y>sdomx,所以 sdomx 以上半支配 q​​ 的点都不能经过了,剩下来的相当于上面那种情况的局面,可以类似上面无限循环来归谬。

实现

sdom​ 的部分,按 dfs 序从后往前更新显然是无后效性的。查询上开下闭的链上最值怎么维护?树剖?可以巧妙地使用带权并查集(Tarjan 又一次使用自己的成果!),每更新完一个点就把它往父亲合并,易证更新到 x​ 的时候,y​ 已经合并到了 LCA(x,y)​。​并查集只能路径压缩,不能按秩合并,所以复杂度是一个 log

idom 的部分恰好也涉及上开下闭的链上最值查询,可以窃取之前的并查集成果。但是查询完之后需要更新的 idom 信息应当是按 dfs 序从前往后更新才是没有后效性的。所以我们可以先把并查集该查的查好存起来(在更新到 sdomx 的时候恰好是 x 的可查状态,可以用 vector 记录 todo-list),结束之后再从前往后扫一遍。

注意点:1 有可能不能到达所有点,枚举 dfs 序的时候一定是到 nowdfn instead of n。初始化的时候应该把所有点的 dfs 序赋为 +​,这样在选取「dfs 序最小的半支配点」时比较函数不会出错。

mol ban tea & code.

支配树求必经边

想不到吧,这玩意还能求必经边。

方法是边点转化,(x,y) 拆成 (x,new),(new,y),这样若 new 是支配点,则 (x,y) 是必经边,易证正确性。这时候你不禁想起无向图的两种连通性,边双和点双,边双这么简单,点双这么难,是不是也可以用点边转化将点双转化为边双呢?说的好像有道理,但你给我翻译翻译什么叫 tmd 无向图点边转化?最大流最小割定理证明无向图的点版本门杰定理中提到的点边转化,那也是先变成有向图再点边转化的呀 2333。

posted @   ycx060617  阅读(413)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
历史上的今天:
2020-08-25 标记永久化 学会了祭
点击右上角即可分享
微信分享提示