Learning Algorithms

アルゴリズムの勉強メモ

二重辺連結成分分解

二重辺連結成分分解の実装です。

二重頂点連結成分分解についてはこちらの記事をご覧ください。

無向グラフ上で、橋が存在しない部分グラフを一つの頂点につぶすと、橋を辺とする木ができます。以下は、それをそのまま実装しています。

struct LowLink {
        set<pair<int, int>> bridge;
        vector<int> articulation, ord, low;
        vector<bool> used;
        vector<vector<int>> g;
        int n, k = 0;
        LowLink(const vector<vector<int>> &g) : g(g) {
                n = g.size();
                ord.resize(n, 0);
                low.resize(n, 0);
                used.resize(n, false);
        }
        void dfs(int u, int prev) {
                used[u] = true;
                ord[u] = k ++;
                low[u] = ord[u];
                bool is_articulation = false;
                int cnt = 0;
                for (auto v : g[u]) if (v != prev) {
                        if (!used[v]) {
                                cnt ++;
                                dfs(v, u);
                                low[u] = min(low[u], low[v]);
                                if (low[v] > ord[u]) {
                                        bridge.emplace(min(u, v), max(u, v));
                                } 
                                if (prev != -1 && low[v] >= ord[u]) {
                                        is_articulation = true;
                                }
                        } else {
                                low[u] = min(low[u], ord[v]);
                        }
                }
                if (prev == -1 && cnt > 1) is_articulation = true;
                if (is_articulation) articulation.push_back(u);
        }
};
struct TwoEdgeConnectedComponent {
        int n;
        vector<vector<int>> g, tree;
        vector<int> cmp;
        TwoEdgeConnectedComponent(const vector<vector<int>> &g) : g(g) {
                n = (int) g.size();
                cmp.assign(n, -1);
        }
        void build() {
                LowLink lnk(g);
                lnk.dfs(0, -1);
                int k = 0;
                function<void (int, int)> dfs = [&](int u, int prev) {
                        cmp[u] = k;
                        for (auto v : g[u]) if (cmp[v] == -1 && lnk.bridge.count({min(u, v), max(u, v)}) == 0) {
                                dfs(v, u);
                        }
                };
                for (int i = 0; i < n; i ++) if (cmp[i] == -1) {
                        dfs(i, -1);
                        k ++;
                }
                tree.resize(k);
                for (auto e : lnk.bridge) {
                        tree[cmp[e.first]].push_back(cmp[e.second]);
                        tree[cmp[e.second]].push_back(cmp[e.first]);
                }
        }
};

以下のような問題が簡潔に実装できます。
D - 旅行会社高橋君

int main() {
        int n, m;
        scanf("%d%d", &n, &m);
        vector<vector<int>> g(n);
        for (int i = 0; i < m; i ++) {
                int a, b;
                scanf("%d%d", &a, &b);
                a --, b --;
                g[a].push_back(b);
                g[b].push_back(a);
        }
        TwoEdgeConnectedComponent tecc(g);
        tecc.build();
        LCA lca(0, tecc.tree);
        int q;
        scanf("%d", &q);
        while (q --) {
                int a, b, c;
                scanf("%d%d%d", &a, &b, &c);
                a --, b --, c --;
                if (lca.dist(tecc.cmp[a], tecc.cmp[b]) + lca.dist(tecc.cmp[b], tecc.cmp[c]) == lca.dist(tecc.cmp[a], tecc.cmp[c])) {
                        printf("OK\n");
                } else {
                        printf("NG\n");
                }
        }
        return 0;
}