为了方便本文的叙述,做出如下可能不严谨的定义:
对于一棵树,我们可以用([x,y])简洁的表示从(x)到(y)的路径上的所有点组成的集合,假如我们希望这个集合不包含(x)或(y),只要将闭区间改为开区间即可。如([x,y))表示从(x)到(y)的路径上的所有点(不包含(y))组成的集合。
我们从一道简单的板子题(在读完这篇文章以后就是了)开始:
给定一个(n)个点,(m)条边的有向图(G),以及一个出发点(r)。询问(q)次,每次给定点(x),(y),保证从(r)出发可以到达(x)。问从(r)出发到达(x)的所有路径是否都需要经过(y)。
(n,m,qleq 10^5)。
为了解决这个问题,我们定义有向图上的支配关系:对于一个固定的出发点(r),如果从(r)出发到达(x)的所有路径都需要经过(y),则称(y)是(x)的支配点,即(y)支配(x)。特别的,我们认为(x)不是(x)的支配点。
我们来研究一下支配关系有哪些性质:
首先,我们将以(r)为根的(dfs)树建立出来。容易发现,对于任意一个点(x),它的所有支配点只可能是它的祖先——显然,对于不是它的祖先的任意一个点(y),我们从(r)通过树边到达(x)就不需要经过(y)。
接着我们假设(y)是(x)的支配点中深度最大的(即距离(x)最近的),那么还可以发现:对于(z eq y),(z)是(x)的支配点当且仅当(z)是(y)的支配点。
证明如下:
首先证明充分性。如果(y)支配(x),(z)支配(y),那么假设(z)不支配(x),即存在一条路径不经过(z)。那么显然由于这条路径必定经过(y),于是我们就找到了一条不经过(z)到达(y)的路径,这与前提条件矛盾,于是假设不成立,充分性得证。
再证明必要性。如果(y)支配(x),(z)不支配(y),显然可以找到一条路径不经过(z)而到达(y)。此时由于(y)是深度最大的(x)的支配点,因此沿着树边一直走到(x),必定不会经过(z),于是(z)就不可能是(x)的支配点。
于是我们就可以知道,对于任意一个点(x),我们只要找到它深度最大的支配点(y),其余的支配点就都是(y)的支配点。不难发现,这样的支配关系形成了一个类似树的结构,将之前所说的(y)当作(x)的父亲的话,那么(x)的所有支配点就是这棵树上它的祖先。我们将这棵树叫做支配树,将这样的(y)记为(idom(x))。
那么问题来了,如何求出(idom(x))呢?
为了解决这个问题,我们先定义一个叫做半支配的东西:
对于任意一个点(x),从(y)出发,只经过不是(x)的祖先的点就可以到达(x)(不包含(x),(y)),则称(y)是(x)的半支配点,即(y)半支配(x)。
首先,我们可以发现(x)的深度最小的半支配点一定是(x)的祖先。因为假设(x)的深度最小的半支配点(y)不是(x)的祖先,那么显然我们可以沿着树边往回走,一直走到某个(x)的祖先(z)上。这其间肯定不会经过除(z)以外的(x)的祖先点,于是(z)的深度比(y)小,且也是(x)的半支配点,于是假设不成立,得证。
接着,如果(y)是(x)的深度最小的半支配点,那么树上路径((x,y))中的点就显然不是(x)的支配点——因为从(y)出发存在一条路不经过它们而到达(x)。我们将这样的(y)记为(sdom(x))。
我们来考虑如果求得了半支配点,那么如何求支配点。考虑对于一个点(x),树上路径((x,sdom(x)))中的点一定不是(x)的支配点。并且对于任意一个(x)的祖先(y),树上路径((y,sdom(y)))中的点也一定不是(x)的支配点。反之,如果某个(x)的祖先(z)不被((x,sdom(x)))和任何一个((y,sdom(y)))包含,那么(z)一定是(x)的支配点——否则一定会存在一条不经过(z)的路径(S)到达(x),(S)中一定存在两个(x)的祖先(a,b)使得树上路径((a,b))经过(z)而(a,b)之间不经过其它(x)的祖先,于是(z)一定会被某个区间包含。
于是(idom(x))就是不被如上所述的这些区间包含的(x)的祖先中深度最大的。考虑递归解决这个问题,如果(y)是([x,sdom(x)))中(sdom(y))的深度最小的,那么如果(sdom(y))就是(sdom(x)),显然(sdom(x))不会被任何区间包含了,那么(idom(x))就是(sdom(x))。否则我们就去掉区间((x,sdom(x)))并求对于(y)来说这个问题的答案即可——而这就是(idom(y)),已经求好了。
好,现在解决最后一个问题,如何求出(sdom(x))。分两种情况讨论:
(1))最后一条边为前向边或树边。显然另一端就是(x)的祖先,路径必须结束。这种情况可以直接得到半支配点。
(2))最后一条边为横插边或后向边,设走到的点为(y),而(x),(y)在(dfs)树上的(lca)为(z)。由于(lca)为(z),因此树上路径([x,z))中的点一定无法到达([y,z))中的点,于是对于([y,z))的(sdom)来说,不会有经过([x,z))中的点的路径,那么显然树上路径([y,z))中的点对于(x)来说都是合法的。于是用树上路径([y,z))中的点的(sdom)来更新(sdom(x))即可。可以发现沿着时间戳反向求(sdom)的话,我们求(sdom)的顺序就不会冲突。
于是我们就解决了文章开头提到的那个问题。至于具体的实现,我们在反向求(sdom)的过程中可以用带权并查集维护一段树上路径的(sdom)最小值,在求完一个点的(sdom)以后将它的儿子与它合并即可。这样每次查询时并查集维护的恰好就是我们需要的那一条链。至于求(idom)的过程,我们需要知道([x,sdom(x)))的信息,于是将请求接入(sdom(x))的链表中。每一次求完(x)的(sdom)后,我们扫描其父亲的请求链表,对于链表中每一个(y),可以发现带权并查集此时维护的就是我们要查询的区间([y,sdom(y))),直接从带权并查集上得知(sdom)最小的为哪一个并存下来并清空链表,在最后求(idom)时直接用即可。
时间复杂度(O((n+m)log n))。
有向图上还有类似的支配边的关系,容易发现只有树边能成为支配边。且如果一条边(y o x)为(x)的支配边,需要(y)为(x)的支配点,且对于所有有非树边指向(x)的点(z),都有(x)支配(z)。
有向图上还有割点、桥、点双、边双的定义。不过还是下次填坑吧...