【图论】边双连通分量

不存在桥的图,称为边双连通图。原图可以经过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的),但是这个是错的,例如一个“天”字形的图。

posted @ 2021-02-18 02:01  purinliang  阅读(125)  评论(0编辑  收藏  举报