cp-library

This documentation is automatically generated by online-judge-tools/verification-helper

View the Project on GitHub rniya/cp-library

:heavy_check_mark: Strongly Connected Components
(src/graph/StronglyConnectedComponents.hpp)

概要

有向グラフを強連結成分に分解する. 各強連結成分の番号は強連結成分を 1 つの頂点にするように縮約した DAG におけるトポロジカル順序を同時に表す. 実装は Tarjan の考案したアルゴリズムに沿っている.

メンバ関数 効果 時間計算量
StronglyConnectedComponents(n) $n$ 頂点 $0$ 辺のグラフとして初期化する. $O(n)$
add_edge(u, v) 頂点 $u$ から頂点 $v$ への有向辺を追加する. $O(1)$
build() 強連結成分分解, 各強連結成分の頂点集合を返す. $O(n + m)$
make_graph() 各強連結成分を頂点とする DAG の辺情報を返す. $O(n + m + m \log m)$

make_graph では利便性を考えてソートして多重辺を除去しているが,特にその必要もなく時間計算量が問題であればこのパートは除いて良い.

Required by

Verified with

Code

#pragma once
#include <algorithm>
#include <cassert>
#include <vector>

struct StronglyConnectedComponents {
    std::vector<std::vector<int>> G;
    std::vector<int> comp;  // component id vertex v belongs to

    StronglyConnectedComponents(int n) : G(n), comp(n, -1), n(n), time(0), group_num(0), ord(n, -1), low(n) {}

    void add_edge(int u, int v) {
        assert(0 <= u && u < n);
        assert(0 <= v && v < n);
        G[u].emplace_back(v);
    }

    int add_vertex() {
        G.emplace_back();
        comp.emplace_back(-1);
        ord.emplace_back(-1);
        low.emplace_back();
        return n++;
    }

    std::vector<std::vector<int>> build() {
        for (int i = 0; i < n; i++) {
            if (ord[i] < 0) {
                dfs(i);
            }
        }
        for (int& x : comp) x = group_num - 1 - x;
        std::vector<std::vector<int>> groups(group_num);
        for (int i = 0; i < n; i++) groups[comp[i]].emplace_back(i);
        return groups;
    }

    std::vector<std::vector<int>> make_graph() {
        std::vector<std::vector<int>> dag(group_num);
        for (int v = 0; v < n; v++) {
            for (int& u : G[v]) {
                if (comp[v] != comp[u]) {
                    dag[comp[v]].emplace_back(comp[u]);
                }
            }
        }
        for (auto& to : dag) {
            std::sort(to.begin(), to.end());
            to.erase(unique(to.begin(), to.end()), to.end());
        }
        return dag;
    }

    int operator[](int v) const { return comp[v]; }

private:
    int n, time, group_num;
    std::vector<int> ord, low, visited;

    void dfs(int v) {
        ord[v] = low[v] = time++;
        visited.emplace_back(v);
        for (int& u : G[v]) {
            if (ord[u] == -1) {
                dfs(u);
                low[v] = std::min(low[v], low[u]);
            } else if (comp[u] < 0) {
                low[v] = std::min(low[v], ord[u]);
            }
        }
        if (ord[v] == low[v]) {
            while (true) {
                int u = visited.back();
                visited.pop_back();
                comp[u] = group_num;
                if (u == v) break;
            }
            group_num++;
        }
    }
};
#line 2 "src/graph/StronglyConnectedComponents.hpp"
#include <algorithm>
#include <cassert>
#include <vector>

struct StronglyConnectedComponents {
    std::vector<std::vector<int>> G;
    std::vector<int> comp;  // component id vertex v belongs to

    StronglyConnectedComponents(int n) : G(n), comp(n, -1), n(n), time(0), group_num(0), ord(n, -1), low(n) {}

    void add_edge(int u, int v) {
        assert(0 <= u && u < n);
        assert(0 <= v && v < n);
        G[u].emplace_back(v);
    }

    int add_vertex() {
        G.emplace_back();
        comp.emplace_back(-1);
        ord.emplace_back(-1);
        low.emplace_back();
        return n++;
    }

    std::vector<std::vector<int>> build() {
        for (int i = 0; i < n; i++) {
            if (ord[i] < 0) {
                dfs(i);
            }
        }
        for (int& x : comp) x = group_num - 1 - x;
        std::vector<std::vector<int>> groups(group_num);
        for (int i = 0; i < n; i++) groups[comp[i]].emplace_back(i);
        return groups;
    }

    std::vector<std::vector<int>> make_graph() {
        std::vector<std::vector<int>> dag(group_num);
        for (int v = 0; v < n; v++) {
            for (int& u : G[v]) {
                if (comp[v] != comp[u]) {
                    dag[comp[v]].emplace_back(comp[u]);
                }
            }
        }
        for (auto& to : dag) {
            std::sort(to.begin(), to.end());
            to.erase(unique(to.begin(), to.end()), to.end());
        }
        return dag;
    }

    int operator[](int v) const { return comp[v]; }

private:
    int n, time, group_num;
    std::vector<int> ord, low, visited;

    void dfs(int v) {
        ord[v] = low[v] = time++;
        visited.emplace_back(v);
        for (int& u : G[v]) {
            if (ord[u] == -1) {
                dfs(u);
                low[v] = std::min(low[v], low[u]);
            } else if (comp[u] < 0) {
                low[v] = std::min(low[v], ord[u]);
            }
        }
        if (ord[v] == low[v]) {
            while (true) {
                int u = visited.back();
                visited.pop_back();
                comp[u] = group_num;
                if (u == v) break;
            }
            group_num++;
        }
    }
};
Back to top page