$\newcommand{\O}{\mathrm{O}}$
有向グラフの強連結成分分解を行うアルゴリズム. また強連結成分分解後の各強連結成分を頂点とする DAG を, 頂点をトポロジカル順序に番号付けした形で求める.
実装は逆辺からなるグラフを持つ必要のない Tarjan のアルゴリズムの方を用いている.
(関数)
add_edge$(u, v)$ : 有向辺$(u,v)$ を追加する
make_graph(): solve() 後に呼び出すと各強連結成分を頂点とする DAG が $graph$ に入る
時間計算量: $\O (n+m)$
class SCC { private: const int V; vector<vector<int> > G; vector<int> ord, low; stack<int> st; void dfs(const int u, int& tm){ ord[u] = low[u] = tm++, st.push(u); for(int v : G[u]){ if(ord[v] < 0){ dfs(v, tm); low[u] = min(low[u], low[v]); }else if(cmp[v] < 0){ low[u] = min(low[u], ord[v]); } } if(ord[u] == low[u]){ while(true){ const int v = st.top(); st.pop(); cmp[v] = cnt; if(v == u) break; } ++cnt; } } public: vector<vector<int> > graph; vector<int> cmp; int cnt; SCC(const int node_size) : V(node_size), G(V), ord(V, -1), low(V), cmp(V, -1), cnt(0){} void add_edge(const int from, const int to){ G[from].push_back(to); } int solve(){ int tm = 0; for(int i = 0; i < V; ++i){ if(ord[i] < 0) dfs(i, tm); } for(int i = 0; i < V; ++i) cmp[i] = cnt - 1 - cmp[i]; return cnt; } void make_graph(){ graph.resize(cnt); for(int i = 0; i < V; ++i){ for(int j : G[i]){ if(cmp[i] != cmp[j]){ graph[cmp[i]].push_back(cmp[j]); } } } } };
AOJ : Strongly Connected Components
提出コード
yosupo さんの library checker : Strongly Connected Components
提出コード