• [算法笔记] 双连通分量


    由于想把文章写的短一点,所以把割点和这篇分开了。
    在阅读本文之前,请先阅读割点与割边

    边双联通分量 Edge Double Connected Component

    先讲边双是因为它比点双简单

    性质

    先给定义:不存在割边极大双连通子图
    (再加入任何一个/一些点后,它都会不连通或出现割边,不再是 e-DCC)
    画张图吧,好理解
    tTAaGR.png
    红边是割边,圈出来的是三个双联通分量。

    性质:

    • 边双连通分量中任意一条边包含在至少一个简单环

    这条性质也是边双的充要条件。

    分别证明,首先若该性质满足,那么删去任意一条边不会影响这个环中点之间两两可达性,这样任意一条边都不为割边了,必为边双。
    然后,若图为边双连通图,反证,如果 ((u,v)) 不包含在任意一个简单环中,断掉该变后,(u) 没有路径可以到达 (v),图不再连通。

    但是边双中并不一定包含一个经过所有点的简单环,点双也是,反例如下。
    tTVo5Q.png

    求法

    把所有割边断掉,剩下每个连通块各为一个边双连通分量。

    边双缩点:边双树

    就像强联通分量缩点一样,我们可以把每一个边双缩成一个点来处理一些问题,然后这些点之间由原来的割边相连形成了一棵树。

    最后得出一定是一棵树(森林),如果不是树,那就有环,环就是个边双。

    就像强连通分量分解把一般有向图变成了有向无环图一样,我们把无向图的问题转到了树上,更好处理了,同时也保存了原来无向图的信息。

    例题:codeforces 555E Case of Computer Network

    首先缩点成边双森林,定根,每条路径拆成向上和向下两部分,如果有一条边既要向上又要向下,那就无解了。

    这个用树上差分实现,维护点信息对应其父边信息。

    这里简单地讲一下如果要求具体方案的做法。
    对于割边,看覆盖这条边的路径是向上还是向下的即可。

    边双经过定向可以成为一个强连通分量。
    无向图的 dfs 树只有返祖边和树边,没有横叉边。
    把边双单独拿出来建个 dfs 树的话,树边向儿子,返祖边向祖先连即可。
    一路往下走到叶子之后,一定会存在一条路能够回到根,否则这个叶子就不会在边双里面了(由于没有横叉边,它能走到的所有点是一棵完整的子树,且该子树根不是树的根),到根以后,就可以往下走到任何一个节点了。

    点双联通分量 Vertex Double Connected Component

    其实和前者非常的相似

    性质

    先给定义:不存在割点的极大双连通子图
    画张图吧,好理解
    tTEfk4.png

    性质:

    • 点双连通分量中任意两点同时包含在至少一个简单环

    等价于两点间存在两条不相交(没有公共点)的路径。
    这条性质也是点双的充要条件。

    若任意两点间存在两条不相交的路径,那么不存在同时在两条路径上的点,两点一定能互达,所以图中不存在割点,即图为点双连通图。
    若图为点双连通图,继续反证,假设存在两点 (u,v) 它们不同时处于任一简单环中,如果这两点间只有一条路径,那么必存在割点,如果有大于等于两条路径,这些路径若不相交于 (u,v) 以外的点就一定会成环,所以这些路径一定会交于某一点,这样这个点就成为了割点,与点双连通图矛盾。

    • 一个点双中的两不同点之间一定存在简单路径,使得该路径经过在同一个点双内的另一点
    • 两点双连通分量之间如果有公共点,这个公共点一定是唯一的,且一定是割点

    注意下面这两个图是符合定义的,但是会不符合性质,是特例。
    tTZKGd.png

    求法

    如果仿照上面的做法,把所有割点断掉,剩下每个连通块各为一个点双。
    但这是错的,因为割边不包含于任一边双中,但是割点一定包含于至少两个点双中。
    这个做法相对比较复杂。

    还是用 Tarjan 算法,维护一个栈,对整张图做 dfs。

    1. 若一个节点第一次被访问,将其入栈
    2. 在割点处理过程中,在处理 (u) 的儿子 (v) 时,如果遇到 (dfn_uleq low_v),则从栈顶不断弹出节点直到 (v) 被弹出为止((v) 要弹出)这些点和 (u) 一起构成一个点双连通分量

    注意到这里没有弹出 (u),因此此时 (u) 是一个割点,计算它和它子树中节点组成的点双时不能弹出,因为它包含于不止一个双联通分量,只有在处理它某个祖先节点时,才会弹出这个节点,这样处理完它的子树的 v-DCC 后再处理和它祖先构成的 v-DCC 最后弹出,就可以计入所有 v-DCC 了。这个点作为割点,也是必定会包含在上面的那个点双里的(根节点除外)。

    void tarjan(LL u,LL fa){
        low[u] = dfn[u] = ++ ti;
        stk[++ top] = u;
        LL tot = 0;
        for(LL i = hed[u];i;i = nxt[i]){
            LL v = to[i];
            if(!dfn[v]){
                ++ tot; tarjan(v,fa);
                low[u] = min(low[u],low[v]);
                if((u == fa && tot >= 2) || (u != fa && dfn[u] <= low[v])) cut[u] = 1;
                if(dfn[u] <= low[v]){
                	bc ++; blk[bc].clear();
                	do{
                	      blk[bc].push_back(stk[top --]);
    		}while(stk[top + 1] != v);
    		blk[bc].push_back(u);
    	      }
            }
            else low[u] = min(low[u],dfn[v]);
        }
    }
    

    圆方树

    与边双树相对应。
    在处理点双的问题时,往往会使用圆方树,将图的问题转化为树上问题,并很好的保留了原图的信息。
    对每一个点双,建立一个方点,将这个方点与原来点双中的所有点相连,连成一个菊花图来处理,这样可以得出一棵树。(如果成环了,相应的,这个环也能缩成一个点双,所以必定是树)

    借用 WC ppt 的一张图
    tTeJ61.png
    这超出了本文的讨论范围了,感兴趣的读者可以自行查询资料学习。
    说白了,就是我不会啊

  • 相关阅读:
    C++ Primer 笔记——标准库类型string
    Windows文件系统
    c++数组
    B+树
    简单搭建FastDFS分布式文件系统(简单易懂)
    什么是分布式系统(通俗易懂)
    对List中每个对象元素按时间顺序排序
    java23种设计模式之一: 策略模式
    微信app支付java后台流程、原理分析及nei网穿透
    quartz多任务调度+spring 实现
  • 原文地址:https://www.cnblogs.com/IltzInstallBI/p/13113566.html
Copyright © 2020-2023  润新知