$\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
提出コード