zoukankan      html  css  js  c++  java
  • 【图论】边双连通分量

    不存在桥的图,称为边双连通图。原图可以经过tarjan算法求出桥,桥就连接了两个边双连通分量,假如把桥的都堵住(注意是只能把桥堵住,不能堵住点),然后每一块找一次连通块,就可以找到每个点所属的双连通分量,然后可以缩点成一棵树。

    struct EBCC {
    
        static const int MAXN = 2e5 + 10;
        static const int MAXM = 2e6 + 10;
    
        int n, m, t;
    
        struct Edge {
            int v, nxt;
        } e[MAXM << 1];
        int h[MAXN];
    
        int dfn[MAXN], low[MAXN];
        bool brg[MAXM];
    
        int col[MAXN];
        vector<int> ebcc[MAXN];
    
        void Init(int n) {
            this->n = n, m = 1;
            for(int i = 1; i <= n; ++i)
                h[i] = 0;
        }
    
        void AddEdge(int u, int v) {
            if(u == v) {
                m += 2;
                return;
            }
            e[++m] = {v, h[u]}, h[u] = m;
            e[++m] = {u, h[v]}, h[v] = m;
        }
    
        void dfs(int u, int from) {
            dfn[u] = low[u] = ++t;
            for(int i = h[u]; i; i = e[i].nxt) {
                int v = e[i].v;
                if(!dfn[v]) {
                    dfs(v, i ^ 1);
                    low[u] = min(low[u], low[v]);
                    if(low[v] > dfn[u])
                        brg[i / 2] = 1;
                } else if(i != from)
                    low[u] = min(low[u], dfn[v]);
            }
        }
    
        void GetBridges() {
            t = 0;
            for(int i = 1; i <= n; ++i)
                dfn[i] = low[i] = 0;
            for(int i = 2; i <= m; i += 2)
                brg[i / 2] = 0;
            for(int i = 1; i <= n; ++i) {
                if(!dfn[i])
                    dfs(i, 0);
            }
        }
    
        void dfs2(int u, int color) {
            col[u] = color, ebcc[color].eb(u);
            for(int i = h[u]; i; i = e[i].nxt) {
                if(brg[i / 2])
                    continue;
                int v = e[i].v;
                if(!col[v])
                    dfs(v, color);
            }
        }
    
        void GetEBCC() {
            GetBridges();
            for(int i = 1; i <= n; ++i)
                col[i] = 0, ebcc[i].clear();
            int n2 = 0;
            for(int i = 1; i <= n; ++i) {
                if(!col[i]) {
                    ++n2;
                    dfs2(i, n2);
                }
            }
            for(int i = 2; i <= m; i += 2) {
                if(brg[i / 2]) {
                    int u = e[i].v, v = e[i + 1].v;
                    // AddBridge(u,v)
                }
            }
        }
    
    } ebcc;
    

    问题:加最少的边,使得无向图变成边双连通图。

    首先,缩点。然后特判剩下的树是否只有1个点,是就返回。

    否则,从根开始dfs给叶子们编号。假如树有cnt个叶子,若cnt为偶数,那么第i个叶子和第i+cnt/2个叶子连边(保证至少有一对点的LCA跨越根,并且根的每条出边都被跨越)。 但是为什么呢。dfs序中,一个子树是dfs区间是连续的。

    有的算法是说找每次LCA最高的两个叶子连接(例如Claris的),但是这个是错的,例如一个“天”字形的图。

  • 相关阅读:
    vue-nuxtjs
    mongodb4.0支持事务
    promisify,promisifyAll,promise.all实现原理
    nodejs, 阿里oss上传下载图片
    数据库备份与还原
    SQL 数据类型、约束、索引及视图
    数据库的查询
    数据库(增、删、改、查)
    数据库基础知识
    C#语言小结
  • 原文地址:https://www.cnblogs.com/purinliang/p/14410418.html
Copyright © 2011-2022 走看看