看某个ppt的时候看到了这个东西,就产生了一些兴趣。

这个东西似乎有点鸡肋,我做了这么久的题,没有见过一道是用支配树解决的。

不过这个算法真的很有趣。

现在我还没有学完。打打博客加深理解。



什么是支配树?

现在我给你一个起点为rrr的有向图,你需要求各种有关必经点的问题。

先说一堆概念:

支配点:对于一个点xxx,如果rrr到xxx的所有路径上经过yyy,那么yyy支配xxx

最近支配点:对于一个点xxx,如果有一个点yyy满足yyy支配xxx,并且所有支配xxx的平凡支配点全都支配yyy,那么yyy就是xxx的最近必经点,记作idom(x)idom(x)idom(x)

平凡支配点就是除了xxx,之外的所有支配点。(其实网上的其它博客说rrr也不包括在内,不过我觉得这样方便理解)

定理1:对于任意的点,它的最近支配点都是唯一的。

证明:假设这个点是xxx,它有两个最近支配点分别为yyy和zzz
由定义得zzz支配yyy,并且yyy支配zzz。
因为y≠zy\neq zy̸​=z,所以矛盾

这个证明是不是特别简略……

既然每个点的最近支配点都是唯一的,那我们自然而然地想到可以建立一棵树。

对于点xxx,将idom(x)idom(x)idom(x)作为自己的父亲,方向为idom(x)idom(x)idom(x)到xxx。

这就是支配树。

性质?

对于一个点xxx,所有支配它的点都是它在支配树上的祖先。

所以说我们可以利用它这个奇妙的性质做各种各样有关必经点的事情。

知道了支配树的功能,现在我们的主要问题是,如何建立支配树。

建立支配树就需要更多的概念和性质……



更加详细的性质

首先,从rrr开始,建立一棵dfs树

显然这个dfs树并不是支配树……

记录一下它们的dfs序,在后面的叙述中,我直接用dfs序来表示它们的编号。

一棵图中的边(x,y)(x,y)(x,y)有四种情况:树枝边(这里指的是dfs树),前向边(xxx是yyy的祖先),后向边(xxx是yyy的后代),横插边(显然x>yx>yx>y)。

接下来我们用几个符号来表示各种关系(这个一定要记住):

a→ba\to ba→b表示aaa能直接通过一条边到达bbb

a⇝ba \leadsto ba⇝b表示aaa能通过某条路径到达bbb

a→˙ba \dot \to ba→˙b表示aaa能通过树枝边到达bbb

a→+ba \overset{+}{\to}ba→+b表示a→˙ba \dot \to ba→˙b并且a≠ba\neq ba̸​=b

引理1(路径引理)

对于两个点vvv和www,若v≤wv\leq wv≤w,那么vvv到www的路径上必定经过他们的公共祖先(也就是LCA(v,w)LCA(v,w)LCA(v,w)及其所有祖先)

证明:如果vvv是www的祖先,显然成立。
因为横插边都是从大点去到小点,所以不可以通过横插边从vvv所在的子树到达www所在的子树。
所以只能通过后向边跳上公共祖先,然后从公共祖先通过前向边跳下来。

半支配点

对于w≠rw\neq rw̸​=r,都有一个半支配点sdom(w)sdom(w)sdom(w)

sdom(w)=min⁡{v∣∃(v0,v1,⋯ ,vk−1,vk),v0=v,vk=w,∀1≤i≤k−1,vi>w}sdom(w)=\min\{ v | \exists (v_0,v_1,\cdots,v_{k-1},v_k), v_0 = v, v_k = w, \forall 1 \leq i \leq k-1, v_i>w \}sdom(w)=min{v∣∃(v0​,v1​,⋯,vk−1​,vk​),v0​=v,vk​=w,∀1≤i≤k−1,vi​>w}

也就是说,从sdom(w)sdom(w)sdom(w)开始,存在一条从sdom(w)sdom(w)sdom(w)到www的路径,使得中间经过的所有点都比www大。

注意,半支配点并不一定是支配点,可能是支配点。



引理2

对于任意w≠rw\neq rw̸​=r,满足idom(w)→+widom(w) \overset{+}{\to} widom(w)→+w

(证明显然:如果不是这样,它就可以直接通过树枝边来到达www了,与定义矛盾)

引理3

对于任意w≠rw\neq rw̸​=r,满足sdom(w)→+wsdom(w) \overset{+}{\to} wsdom(w)→+w

证明:首先,fawfa_wfaw​是sdom(w)sdom(w)sdom(w)的一个候选,因为中间没有任何的点落脚。
所以sdom(w)≤fawsdom(w)\leq fa_wsdom(w)≤faw​
根据路径引理,如果sdom(w)sdom(w)sdom(w)在另一棵子树,那么必定经过他们的公共祖先,公共祖先小于xxx,与定义矛盾

引理4

对于任意w≠rw \neq rw̸​=r,满足idom(w)→˙sdom(w)idom(w) \dot \to sdom(w)idom(w)→˙sdom(w)

证明:如果不是这样,那么存在路径r⇝sdom(w)⇝wr\leadsto sdom(w)\leadsto wr⇝sdom(w)⇝w,
sdom(w)⇝wsdom(w)\leadsto wsdom(w)⇝w不经过idom(w)idom(w)idom(w),与idomidomidom的定义矛盾

引理5

对于满足v→˙wv \dot \to wv→˙w的点vvv和www,v→˙idom(w)v \dot \to idom(w)v→˙idom(w)或idom(w)→˙idom(v)idom(w)\dot \to idom(v)idom(w)→˙idom(v)

这个似乎有点不好理解,其实可以根据上面的几个引理画出来:

idom(v)→+v→˙idom(w)→+widom(v) \overset{+}{\to}v\dot \to idom(w)\overset{+}{\to}widom(v)→+v→˙idom(w)→+w

idom(w)→˙idom(v)→+v→˙widom(w) \dot \to idom(v) \overset{+}{\to}v\dot \to widom(w)→˙idom(v)→+v→˙w

也就是idom(v)→+vidom(v)\overset{+}{\to}vidom(v)→+v要么被idom(w)→+widom(w)\overset{+}{\to}widom(w)→+w包含,要么不相交。(这样说不严谨,因为端点可能会重合)

证明:如果不是这样,那么idom(v)→+idom(w)→+v→+widom(v) \overset{+}{\to} idom(w)\overset{+}{\to} v\overset{+}{\to} widom(v)→+idom(w)→+v→+w
存在r→˙idom(v)⇝v→+wr \dot \to idom(v) \leadsto v \overset{+}{\to} wr→˙idom(v)⇝v→+w。(idom(w)idom(w)idom(w)是idom(v)idom(v)idom(v)的真后代,不支配vvv。所以可以绕过idom(w)idom(w)idom(w)到达www)
与idomidomidom的定义矛盾。



这后面的会难一些,不对,是难很多。

揭示了idomidomidom和sdomsdomsdom的关系

定理2

对于任意w≠rw\neq rw̸​=r,如果所有满足sdom(w)→+u→˙wsdom(w)\overset{+}{\to}u\dot \to wsdom(w)→+u→˙w的uuu也满足sdom(w)≤sdom(u)sdom(w)\leq sdom(u)sdom(w)≤sdom(u),则idom(w)=sdom(w)idom(w)=sdom(w)idom(w)=sdom(w)

画一下就是:

sdom(w)→˙sdom(u)→+u→˙wsdom(w)\dot \to sdom(u) \overset{+}{\to} u \dot \to wsdom(w)→˙sdom(u)→+u→˙w

证明:由定理4得idom(w)→˙sdom(w)idom(w) \dot \to sdom(w)idom(w)→˙sdom(w),所以如果我们想要证明idom(w)=sdom(w)idom(w)=sdom(w)idom(w)=sdom(w),就只需要证明sdom(w)sdom(w)sdom(w)支配www即可。
对于任意从rrr到www的路径,取最后一个小于sdom(w)sdom(w)sdom(w)的点xxx。
假设yyy是xxx的后继
由于xxx是最后一个小于sdom(w)sdom(w)sdom(w)的点,所以sdom(w)≤ysdom(w) \leq ysdom(w)≤y。
由sdomsdomsdom的定义得,必定存在yyy满足sdom(w)→˙y→˙wsdom(w)\dot \to y \dot \to wsdom(w)→˙y→˙w(否则xxx就是sdom(w)sdom(w)sdom(w))
取最小的yyy,假设yyy不是sdom(w)sdom(w)sdom(w)
由条件得sdom(w)≤sdom(y)sdom(w)\leq sdom(y)sdom(w)≤sdom(y),由于x&lt;sdom(w)x&lt; sdom(w)x<sdom(w),所以x≠sdom(y)x\neq sdom(y)x̸​=sdom(y)
所以存在x→+t→+yx\overset{+}{\to} t\overset{+}{\to} yx→+t→+y,由于xxx是最后一个小于sdom(w)sdom(w)sdom(w)的点,所以sdom(w)→˙t→˙wsdom(w)\dot \to t \dot \to wsdom(w)→˙t→˙w
在前面已经说过yyy是最小的,所以矛盾
因此yyy就是sdom(w)sdom(w)sdom(w)
所以任意路径经过sdom(w)sdom(w)sdom(w),因此sdom(w)sdom(w)sdom(w)支配www。
即idom(w)=sdom(w)idom(w)=sdom(w)idom(w)=sdom(w)

这是前面说过的参考博客的证明,但是我感觉这种证明有点问题。

于是自己用另一种方法证明了一遍:

证明:我们同样是证明sdom(w)sdom(w)sdom(w)支配www
反证法,假设存在路径绕过sdom(w)sdom(w)sdom(w)到达www
这条路径必定可以看作,从rrr绕过sdom(w)sdom(w)sdom(w)到达点xxx(满足sdom(w)→+x→˙wsdom(w)\overset{+}{\to}x \dot \to wsdom(w)→+x→˙w),然后直接到www。
1、当从某个比xxx大的点来到xxx时,由于sdom(w)≤sdom(x)sdom(w) \leq sdom(x)sdom(w)≤sdom(x),所以sdom(w)→˙sdom(x)sdom(w) \dot \to sdom(x)sdom(w)→˙sdom(x),不可能直接通过树枝边从rrr到达sdom(x)sdom(x)sdom(x),不存在这样的路径。
2、当从某个比xxx小的点来到www时,
(1)如果从树枝边走来:
如果xxx是sdom(w)sdom(w)sdom(w)的儿子,那么sdom(x)=sdom(w)sdom(x)=sdom(w)sdom(x)=sdom(w),所以不能到达。
用归纳证明的思想,可得后面的点都不能到达。所以这个点不能在sdom(w)sdom(w)sdom(w)和www之间的路径上。
(2)如果从sdom(w)sdom(w)sdom(w)的子树外走来
这个点显然可以作为sdom(x)sdom(x)sdom(x)的取值,但是sdom(w)≤sdom(x)sdom(w)\leq sdom(x)sdom(w)≤sdom(x),矛盾
综上,这种路径不存在。

可能还是有点不严谨,不过理解一下就好。

定理3

对于任意w≠rw\neq rw̸​=r,设uuu为满足sdom(w)→+u→˙wsdom(w) \overset{+}{\to} u \dot \to wsdom(w)→+u→˙w的sdom(u)sdom(u)sdom(u)最小的uuu,如果sdom(u)≤sdom(w)\leq sdom(w)≤sdom(w),那么idom(w)=idom(u)idom(w)=idom(u)idom(w)=idom(u)

画图:sdom(u)→˙sdom(w)→+u→˙wsdom(u) \dot \to sdom(w) \overset{+}{\to} u \dot \to wsdom(u)→˙sdom(w)→+u→˙w

证明:由引理5得idom(w)→˙idom(u)idom(w) \dot \to idom(u)idom(w)→˙idom(u)或u→˙idom(w)u\dot \to idom(w)u→˙idom(w),由引理444得后面的这种情况不存在。
画张图理解一下:idom(w)→˙idom(u)→˙sdom(w)→+u→˙widom(w)\dot \to idom(u) \dot \to sdom(w)\overset{+}{\to}u \dot \to widom(w)→˙idom(u)→˙sdom(w)→+u→˙w
所以,我们要证明idom(w)=idom(u)idom(w)=idom(u)idom(w)=idom(u),只需要证明idom(u)idom(u)idom(u)支配www即可。
类似于之前的思路:
假设有某一条路径绕过idom(u)idom(u)idom(u)到达www
1、从大于www点到达www
如果这样,存在一个点xxx,满足sdom(x)sdom(x)sdom(x)的定义(不一定最小)。
如果x→+ux \overset{+}{\to} ux→+u,
因为idom(u)idom(u)idom(u)支配uuu,而又存在r⇝x→+ur \leadsto x \overset{+}{\to} ur⇝x→+u,所以idom(u)idom(u)idom(u)支配xxx。
所以不存在某种绕过sdom(w)sdom(w)sdom(w)到达xxx的方案。
如果u→˙xu\dot \to xu→˙x,那么继续考虑能不能到达xxx(像个递归的过程),最终总会到x→+ux\overset{+}{\to} ux→+u的状况,因此也是不存在的。
2、从小于www点到达www
(1)从树枝边走来
类似于之前的情况,如果存在这样的路径,必定能从起点绕过idom(u)idom(u)idom(u)到达uuu和www之间的某个点。(前面已经证明过绕idom(u)idom(u)idom(u)到达idom(u)idom(u)idom(u)和uuu之间的点不存在)
但是连sdom(u)sdom(u)sdom(u)最小的uuu都不成立,那其他的点也不可能了。
(2)从idom(u)idom(u)idom(u)外走来
如果可以这么走,那它就应该是sdom(w)sdom(w)sdom(w),矛盾。
综上所述,不存在这样的路径



推论1

对于任意r≠wr\neq wr̸​=w,如果有满足sdom(w)→+u→˙wsdom(w) \overset{+}{\to} u \dot \to wsdom(w)→+u→˙w且sdom(u)sdom(u)sdom(u)最小的uuu,那么

idom(w)={sdom(w)(sdom(u)=sdom(w))idom(u)(sdom(u)&lt;sdom(w))idom(w) = \left \{ \begin{aligned}&amp; sdom(w)&amp;(sdom(u)=sdom(w))&amp;\\ &amp;idom(u)&amp;(sdom(u)&lt;sdom(w))&amp;\end{aligned} \right .idom(w)={​sdom(w)idom(u)​(sdom(u)=sdom(w))(sdom(u)<sdom(w))​​

这个东西是通过上面的定理2和定理3推过来的。但是在这里,我们发现sdom(u)≤sdom(w)sdom(u)\leq sdom(w)sdom(u)≤sdom(w)的,为什么呢?

我感觉博客中简略的证法好像有问题,所以我自己想了一下:

证明:如果存在某种情况使得sdom(u)&gt;sdom(w)sdom(u)&gt;sdom(w)sdom(u)>sdom(w)
画图就是这样:sdom(w)→+sdom(u)→+u→˙wsdom(w) \overset{+}{\to} sdom(u) \overset{+}{\to} u \dot \to wsdom(w)→+sdom(u)→+u→˙w
显然sdom(sdom(u))&lt;sdom(u)sdom(sdom(u))&lt;sdom(u)sdom(sdom(u))<sdom(u),所以此时的sdom(u)sdom(u)sdom(u)应该是uuu。
我们已经要求过sdom(u)sdom(u)sdom(u)最小,所以矛盾。
因此sdom(u)≤sdom(u)sdom(u)\leq sdom(u)sdom(u)≤sdom(u)



如果暴力求sdomsdomsdom则太慢,于是就有了下面这个强大的定理:

定理4

对于任意w≠rw\neq rw̸​=r,sdom(w)=min({v∣(v,w)∈E,v&lt;w}∪{sdom(u)∣u&gt;w,∃(v,w)∈E,u→˙v})sdom(w) = min(\{v | (v, w) \in E , v &lt; w \} \cup \{sdom(u) | u &gt; w , \exists (v, w) \in E , u \dot \to v\} )sdom(w)=min({v∣(v,w)∈E,v<w}∪{sdom(u)∣u>w,∃(v,w)∈E,u→˙v})

其实这个东西特别像是一个递推式。

别人博客上有证明,我理解式子后,第一反应是,这个东西需要证明?

如果仅仅把它当作一个递推式来看,那么它还是特别好理解的。

证明一下吧……

证明:设等号右边的式子的结果为xxx,显然sdom(w)≤xsdom(w)\leq xsdom(w)≤x。现在要证明x≤sdom(w)x\leq sdom(w)x≤sdom(w)
如果sdom(w)sdom(w)sdom(w)到www中只经过一条边,显然(sdom(w),w)∈E(sdom(w),w)\in E(sdom(w),w)∈E且sdom(w)&lt;wsdom(w)&lt;wsdom(w)<w,所以x≤sdom(w)x\leq sdom(w)x≤sdom(w)
如果不只经过一条边,设这条路径上的最后一个点为lastlastlast。在sdom(w)sdom(w)sdom(w)和lastlastlast之间找到一个最小的点ppp,显然sdom(w)sdom(w)sdom(w)到ppp上经过的点都比ppp大,所以sdom(p)≤sdom(w)sdom(p)\leq sdom(w)sdom(p)≤sdom(w)。
同时sdom(p)sdom(p)sdom(p)满足等式右边的条件,满足p→˙lastp\dot \to lastp→˙last,(last,w)∈E(last,w)\in E(last,w)∈E。
所以sdom(p)sdom(p)sdom(p)是xxx的一个候选,所以x≤sdom(p)≤sdom(w)x\leq sdom(p)\leq sdom(w)x≤sdom(p)≤sdom(w),所以x≤sdom(w)x \leq sdom(w)x≤sdom(w)
综上,x≤sdom(w)x\leq sdom(w)x≤sdom(w)。
所以sdom(w)=xsdom(w)=xsdom(w)=x



Lengauer-Tarjan算法

前面推的东西都是为这个算法铺垫的。这个算法用到定理4和推论1。

先说说步骤:

1、dfs一遍,求出dfndfndfn。

2、按照dfndfndfn倒着求出sdomsdomsdom。

3、确定idom=sdomidom=sdomidom=sdom的idomidomidom,其它的暂时不理它。

4、按照dfndfndfn顺着找没有计算的idomidomidom,计算。

第1步不说。

第2步和第3步可以放在一起来写。

我们要用一个数据结构来维护一个森林,每个点到根的路径上最小的sdomsdomsdom。这个数据结构可以用并查集。我们记作xxx的这个东西为eval(x)eval(x)eval(x)

看看定理4的式子:sdom(w)=min({v∣(v,w)∈E,v&lt;w}∪{sdom(u)∣u&gt;w,∃(v,w)∈E,u→˙v})sdom(w) = min(\{v | (v, w) \in E , v &lt; w \} \cup \{sdom(u) | u &gt; w , \exists (v, w) \in E , u \dot \to v\} )sdom(w)=min({v∣(v,w)∈E,v<w}∪{sdom(u)∣u>w,∃(v,w)∈E,u→˙v})

由于我们是倒过来做的,所以对于点www来说,若它的前驱v&gt;wv&gt;wv>w,那么上式右边的uuu取eval(v)eval(v)eval(v)是最合适的。如果前驱v&lt;wv&lt;wv<w,它们没有处理过,可以把sdomsdomsdom的初值设为它们自己、

通过定理4可以求出所有点的sdomsdomsdom。

接下来将它挂在它的sdomsdomsdom上。每做完一个子树之后,在并查集上将子树和父亲合并,然后处理留在父亲上的sdomsdomsdom,也就是通过引理1来计算idomidomidom。具体来说,如果父亲是xxx,挂在父亲上的点www,它们满足sdom(w)=xsdom(w)=xsdom(w)=x,如果sdom(eval(w))=xsdom(eval(w))=xsdom(eval(w))=x,那么idom(w)=sdom(w)=xidom(w)=sdom(w)=xidom(w)=sdom(w)=x,否则idom(w)=idom(eval(w))idom(w)=idom(eval(w))idom(w)=idom(eval(w)),这个东西先不要求出来,可以在实现的时候记作idom(w)=eval(w)idom(w)=eval(w)idom(w)=eval(w)。

最后就是第4步,按照dfndfndfn从小到大枚举,如果idom(w)=sdom(w)idom(w)=sdom(w)idom(w)=sdom(w),说明idom(w)idom(w)idom(w)已经被处理过,不理它;否则,idom(x)=idom(idom(x))idom(x)=idom(idom(x))idom(x)=idom(idom(x))。

这样就求出所有点的idom(x)idom(x)idom(x)了。

清点一下要维护的东西:

idomidomidom和sdomsdomsdom数组

并查集维护evalevaleval

每个点的前驱predpredpred

将点挂在sdomsdomsdom用的数组(链表)bucbucbuc

以及一些零零散散的东西。

这个算法的时间复杂度实际上是O(nlg⁡n)O(n \lg n)O(nlgn)的,我受到了那篇博客的影响,知道并查集能被卡成lg⁡\lglg级别。当然这种情况大多数是不会见到的,毕竟没几个人来卡你的并查集,就算卡,凭借着并查集的优秀常数也不会多慢。

姑且认为是O(nα(n))O(n \alpha(n))O(nα(n))的,几乎都是这样了。



代码
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 200000
#define M 300000
int n,m;
struct EDGE{
int to;
EDGE *las;
} e[M+1],pe[M+1],te[M+1],be[M+1];
//这四个东西都用链表来存。e表示图边,pe表示反向边(pred),te表示建出来的支配树,be表示buc
int ne,pne,tne,bne;
EDGE *last[N+1],*plast[N+1],*tlast[N+1],*blast[N+1];
int dfn[N+1],nowdfn,ver[N+1]; //dfn为dfs的时间戳,ver为dfs序。
int idom[N+1],sdom[N+1];
int fa[N+1];
void init(int x){//初始化,求出dfn,ver
dfn[x]=++nowdfn,ver[nowdfn]=x;
sdom[x]=x;
for (EDGE *ei=last[x];ei;ei=ei->las)
if (!dfn[ei->to]){
fa[ei->to]=x;
init(ei->to);
}
}
int top[N+1],eval[N+1];//这些就是并查集维护的东西
inline int sdom_min(int a,int b){return dfn[sdom[a]]<dfn[sdom[b]]?a:b;}
inline int dfn_min(int a,int b){return dfn[a]<dfn[b]?a:b;}
void gettop(int x){
if (top[x]==x)
return;
gettop(top[x]);
eval[x]=sdom_min(eval[x],eval[top[x]]);
top[x]=top[top[x]];
}
int siz[N+1];//最后的答案(洛谷的模板题输出支配树中每个点的子树大小)
void get_siz(int x){
siz[x]=1;
for (EDGE *ei=tlast[x];ei;ei=ei->las)
get_siz(ei->to),siz[x]+=siz[ei->to];
}
int main(){
scanf("%d%d",&n,&m);
for (int i=1;i<=m;++i){
int u,v;
scanf("%d%d",&u,&v);
e[++ne]={v,last[u]};
last[u]=e+ne;
pe[++pne]={u,plast[v]};
plast[v]=pe+pne;
}
init(1);
for (int i=1;i<=n;++i)
top[i]=eval[i]=i;
for (int i=n;i>=1;--i){
int x=ver[i],y=fa[x];
for (EDGE *ei=plast[x];ei;ei=ei->las)
gettop(ei->to),sdom[x]=dfn_min(sdom[x],sdom[eval[ei->to]]);//枚举前缀计算sdom
be[++bne]={x,blast[sdom[x]]};//将x挂到sdom[x]上
blast[sdom[x]]=be+bne;
if (y)
for (top[x]=y;blast[y];blast[y]=blast[y]->las){//将自己的子树和父亲合并;清空挂在父亲上的点,用来求idom
int v=blast[y]->to;
gettop(v);
if (sdom[eval[v]]==sdom[v])
idom[v]=sdom[v];
else
idom[v]=eval[v];
}
}
for (int i=1,x=ver[i];i<=n;++i,x=ver[i])//将没有计算完idom的点计算好
if (idom[x]!=sdom[x])
idom[x]=idom[idom[x]];
for (int i=2;i<=n;++i){
te[++tne]={i,tlast[idom[i]]};
tlast[idom[i]]=te+tne;
}
get_siz(1);
for (int i=1;i<=n;++i)
printf("%d ",siz[i]);
return 0;
}