动态平面图边双连通性简介

这个部分是冬令营交流里最后的一部分内容,也是最难的一部分,我觉得看懂这算法对我来说还是有点困难所以先放点简介吧……我还是太菜了 T T

本文尚在施工中,可能会更新


原文:J. Hershberger, M. Rauch, and S. Suri, Fully dynamic 2-edge-connectivity in planar graphs




Topology Tree


看完论文以后才对 Topology Tree 的用途有了一点进一步的认识。之前觉得这个复杂度根号,写起来很麻烦还要处理度数才能用的数据结构到底是拿来干什么的。这个做法里发挥了很多 Topology Tree 的其他很多数据结构难以企及的地方,比如说:

我们可以在 O(\log n) 的时间内把 Topology Tree 在某个节点处 u 展开,展开意味着用一些 Topology Tree 中(互不相交)的 cluster 来代表整棵树(每个 cluster 看成一个点),且这些 cluster 中必须有一个只包含 u 的 cluster 。通过把 Topology Tree 在 u,v 展开我们可以得到一个点数 O(\log n) 的新图(cluster graph),只要我们能把 cluster graph 的信息(每个 cluster 内部, cluster 之间连边)处理出来,那么我们就可以在 cluster graph 上进行一般图可以进行的操作(复杂的修改/询问)了。

在之前看的 Topology Tree 里少提了一点,我们可以要求度数为 3 的 cluster 中只能包含一个点,复杂度不会改变。这就意味着每个 cluster 至多有两个边界节点 v_1,v_2 ,从而穿过这个 cluster 的路径就唯一(即 path(v_1,v_2) ),因此我们可以维护这个路径的相关信息。

基于这些想法,我们来介绍如何用 Topology Tree 维护平面图边双连通性。我们的出发点和之前一致,通过维护生成树中哪些树边被非树边覆盖了来维护双连通性。

以下用小写字母代表树上的节点,大写字母代表 Topology Tree 中的 cluster 节点,如果我们用小写字母(比如 u,v 等) 指代了 Topology Tree 中的某个节点,那就是树上的节点在 Topology Tree 上对应的只包含了它(比如 u,v )的 cluster 节点。

本文中 cluster 和块是等价的。


Coverage Graph


如前所述,我们可以对于每个度数不超过 2 的 cluster 维护有关 path(v_1,v_2) 的信息。我们考虑从所有块内出发到块外的边,则这条边会对 Topology Tree 中 uv 的路径上的所有块产生影响。然而在一个块内我们无法记录所有 (u,v) ,因为有的 v 可能并没有在 cluster graph 上出现,这样会破坏复杂度。但我们发现在 u 所在的块我们并不关心 v 到底是谁,我们可以只用 \mathrm{lca}(u,v) 来描述它所指向的目标(方向)。由于对于一个块 C 而言 LCA 只有 O(\log n) 种,考虑把 LCA 相同的边分为一类。

由于我们并不知道 path(u,v) 在离开 C 的时候到底是从 v_1 出去的还是从 v_2 出去的,我们还是不知道到底有哪些边被覆盖了。不过这些不重要,因为到了 cluster graph 里我们自然就知道这些了。我们考虑所有 LCA 相同的边的 upath(v_1,v_2) 上的投影,令其中最左边的点和最右边的点分别为 l,r ,我们可以发现如果最后从 v_1 出去被覆盖的路径就是 path(v_1,r) ,否则就是 path(l,v_2) 。于是我们可以把 path(l,r) 缩成一个点(无论如何都会被覆盖)。缩点后的 path(v_1,v_2) 称作 coverage graph 。用链表维护所有缩起来的点,对于每个缩起来的点维护和它相关的所有种类的边(缩点左侧的边和右侧的边分别用一个链表)。


Edge Bundles


我们用它们指向的 cluster 和这类边的数量来表示一类边,称其为 Edge Bundle 。如之前所述目标设定为 \mathrm{lca}(u,v) ,称为 LCA targeting 。为了处理修改和询问我们需要把 LCA targeting 改成 precise targeting (即目标直接设定为 v 所在的 cluster ,由于是在修改和询问时 cluster graph 中 v 所在的 cluster 是唯一确定的)。因此我们需要在两种模式之间切换。

从 precise targeting 向 LCA targeting 切换是显然的。对于反向的切换,由于 cluster graph 也是平面的,我们对这棵树做一遍顺时针的欧拉遍历,所有非树边形成了一个其上形如 (((...))) 的括号序列,因此我们可以扫一遍来确定哪些 edge bundle 是同一条边。


Calculating the coverage graphs


为了计算块 C 的 coverage graph 我们先计算儿子的再把它们的合并起来,有以下几种情况:

  1. C 只有一个儿子:什么都不用做
  2. C 有两个度数为 3 和 1 的儿子: C 的度数为 2 ,然而由于度数为 3 和度数为 1 都只有一个点所以直接把度数为 1 的 coverage graph 拿来就好了。
  3. C 有两个度数为 1 的儿子: C 为根,两个儿子的 coverage graph 里也只剩一个 edge bundle 了删了就行了
  4. C 有两个度数为 2 和 1 的儿子:和下一种差不多但少了那种麻烦的情况,一起说
  5. C 有两个度数为 2 的儿子:令 ABC 的儿子,我们需要把连接 A,B 的 edge bundle 删掉,至多有三个 edge bundle :在 path(v_1,v_2) 左侧右侧可能有两个,绕过整个生成树一周可能会有一个。在左侧右侧的 edge bundle 可以通过链表的两端来找到,另外一种如何找到之后会提到。在删除 edge bundle 的时候我们把 path(v_1,v_2) 上对应的部分缩起来。如果有相邻的 edge bundle 有相同的目标也需要缩在一起(可以证明如果两个 edge bundle 有相同的目标且不相邻,那么必然是绕过整个生成树一周的边)。

由于我们无法在所有点都存储一份它自己的 coverage graph ,我们在合并过程中记录合并的过程(称为 recipe),在展开 Topology Tree 需要它儿子的 coverage graph 的时候再撤回即可。


Modification


由于询问是在 cluster graph 上暴力询问一遍就不多说了(由于 cluster graph 是平面的所以点边都是 O(\log n) 的),主要说修改。

几种修改其实都大同小异,先说怎么保证度数为 3 。我们把一个点表示成一个长度为其度数的环,每个环上的点度数为 2 剩下的一个度数用于连出边。注意连的时候不要破坏原图的平面性。

  • 加入边:展开 u,v ,在 cluster graph 里加上这条边
  • 删除非树边:展开 u,v ,在 cluster graph 里删去这条边
  • 删除树边:展开 u,v ,暴力找到一个连接 u,v 两个连通块的 edge bundle ,继续 expand 下去直到我们确定这条用于替换的非树边,用这条非树边替换要删的边,然后把要删的边删掉


进行这些步骤以后剩下的部分都是公共的:在已知修改的情况下更新 Topology Tree 。

首先我们确定修改后新树的结构(但是暂时不对树作出修改),把所有受到影响的 cluster 展开,然后我们切换到 precise targeting 并以此计算新的 LCA targeting ,接下来我们计算受影响的点的 recipe 。由于 LCA 改变,边的 LCA targeting 受到影响,因此一些种类边的 l,r 可能会发生改变。由于受影响的边只有 O(\log n) 条,涉及到需要重建的节点有 O(\log^2 n) 个,我们把它们也展开,从底向顶每次合并两个块,建出新树。


关于合并在计算 coverage graph 的时候我们已经提到过了,但还有一种特殊情况要处理。我们维护每种深度的 edge bundle 有哪些。指向深度为合并后节点的深度的那个 edge bundle 必然就是我们所求的特殊边(事实上 (u,v) 本来就该在 \mathrm{lca}(u,v) 处被删掉)。之后我们暴力把这条边对应路径上的点缩起来。均摊复杂度是 O(\log^2 n) 的。

发布于 2018-01-15

文章被以下专栏收录