求强连通分量和双连通分量,但是不用 Tarjan
1. Kosaraju 算法
Kosaraju 算法用于求有向图的强连通分量。
Kosaraju 算法的码量较小,且在一些问题的处理中更加优秀。流程如下:
- 对原图做 dfs 进行遍历。对每个结点遍历完所有儿子之后再把它记录入栈。
- 建出反图,按照栈的顺序对每个结点进行 dfs 染色。
下面来说明其正确性。熟知在无向图上求连通分量只要直接 dfs。但在有向图上,考虑有向边 \(A\to B\),假设此时遍历到了 \(A\),并发现 \(B\) 还没有被遍历到,那么会经过这条边;但这并不保证存在路径 \(B\rightsquigarrow A\),这样 A 就不能算在强连通分量里面,出现了错误。
Kosaraju 的想法是,选定一个遍历顺序,使得对于 \(A\to B\) 保证得到以下的情况之一:
- 先遍历到 \(B\),再遍历到 \(A\)。
- 先 \(A\) 再 \(B\),但能保证存在 \(B\rightsquigarrow A\)。
反图上的强连通分量是和原图一样的。于是我们可以钦定一个顺序使得对每条不存在从 \(B\) 到 \(A\) 的路径的边 \(A\rarr B\) 先遍历 \(A\) 再遍历 \(B\)(显然这比上面反过来判断的方法好做),然后在反图上 dfs。
记 \(dfn(u)\) 为 Kosaraju 给出的遍历的顺序。这样我们第二次 dfs 的顺序就是这个顺序反过来(因为算法中我们使用栈进行记录)。于是我们只需要证明对原图上的边 \(A\rarr B\), 要么有 \(dfn(A)>dfn(B)\),要么能确定原图上存在另一条从 \(B\) 到 \(A\) 的路径。
对不同的边进行分类讨论。
- dfs 树边/前向边:显然。因为我们总是先记录儿子再记录父亲。
- 后向边:这个时候后向边和树边组成了环,所以顺序随便选。
- 横叉边/不同 dfs 树之间的边:因为这是 dfs 树,我们一定会先遍历到 \(B\) 再遍历到 \(A\)。
证完了。可以用相似的手段来说明其他的遍历方式不可行,如直接的前序 dfs 能处理 dfs 树边、前向边和后向边,但是不能处理横叉边。
upd:Kosaraju 好像在某些题里真的有用,我现在还没完全搞懂。
2. 边双连通分量
这里介绍的算法的好处是思想的简单。
先跑一遍dfs树,然后我们对非树边进行处理。
假设一条非树边连接了 \(u,v\) 两个结点,很明显这条树边和 \(u\) 到 \(v\) 在树上的路径构成了一个环,成为边双连通分量。
于是我们只需要对每条非树边,将树上的对应链打上标记,最后在树上且未被打标记的边就是割边。
边双连通分量只要把割边删了 dfs 就行。
维护链标记的操作考虑树上差分。
注意无向图的 dfs 树没有横叉边,因此直接对深度大的点打上 1,对深度小的打上 -1 就完成了差分操作。
所以我们就轻松做到了线性。
代码可以看我在 luogu 模板题写的题解。
3. 点双连通分量
upd:原来写的误导人的东西删掉了,现在 luogu 点双板子有一篇讲这个的题解。

浙公网安备 33010602011771号