和无向图的连通分量类似,有向图有“强连通分量”的说法。“相互可达”的关系在有向图中也是等价关系。每一个集合称为有向图的一个强连通分量(scc)。如果把一个集合看成一个点,那么所有的scc构成了一个scc图。这个scc图不会存在任何有向环,因此是一个DAG。求解有向图强连通分量的算法一般都是基于dfs的,常用的算法有Kosaraju算法和Tarjan算法,下面给出Tarjan算法的代码:
1 vector G[maxn]; 2 int pre[maxn], low_link[maxn], scc_no[maxn], dfs_clk, scc_cnt; 3 stack S; 4 void dfs(int u){ 5 pre[u] = low_link[u] = ++dfs_clk; 6 S.push(u); 7 FOR(i, 0, G[u].size() - 1){ 8 int v = G[u][i]; 9 if(!pre[v]){10 dfs(v);11 minimize(low_link[u], low_link[v]);12 }else if(!scc_no[v]) minimize(low_link[u], pre[v]);13 }14 if(low_link[u] == pre[u]){15 scc_cnt++;16 while(true){17 int x = S.top(); S.pop();18 scc_no[x] = scc_cnt;19 if(x == u) break;20 }21 }22 }23 void find_scc(int n){24 dfs_clk = scc_cnt = 0;25 clr(scc_no, 0), clr(pre, 0);26 FOR(i, 0, n - 1) if(!pre[i]) dfs(i);27 }
由于每个点恰属于一个scc,因此我们希望在第一次访问某scc的结点并完成时就将该scc输出。所有需要判断某个点是否是其所在scc中最先被发现的点。与计算无向图bcc方法类似,对于每个结点$u$用$lowlink(u)$表示$u$及其后代能够追溯到最早的祖先点$v$的$pre(v)$的值。因此$u$是第一个被发现的点当且仅当$lowlink(u) =pre(u)$。