求有向图强连通分量的一种算法。
算法步骤:
- 用 dfs 遍历整个有向图,取出它的后序遍历(可能是多次dfs,但并不影响,可以直接拼起来)。
- 建反图(原图的每条边反向),按照后序从后往前,以它为起点遍历反图。
- 遍历反图时,所有能够到达的,未经过的点(一定是序列中在它之前出现的)都和起点在一个强连通分量当中。
为什么正确?对反图 $u$ 能到达 $v$ 的两个点 $u,v$(序列中 $v$ 在 $u$ 之前出现)进行讨论。反图 $u$ 能到达 $v$ 也就是原图 $v$ 能到达 $u$。
- 如果原图 $u$ 也能到达 $v$(祖先关系),符合强连通的定义,在一个强连通分量里。
- 如果原图 $u$ 不能到达 $v$(非祖先关系),现在说明原图 $v$ 能到达 $u$,因为是后序,所以 $v$ 在 $u$ 之前且 $u$ 不能到 $v$ 说明 $v$ 已经遍历完了,且 $v$ 不能到 $u$,矛盾。
上面说明了访问到的确是在一个SCC,它一定能遍历所有强连通分量吗?
考虑实际的每个强连通分量在 dfs 中第一个经过的点(不是一次 dfs 到的两个点一定不可能在同一个 SCC)。
因为它是 SCC 中第一个,SCC 的其它点没有被访问,所以它在访问顺序中最先,然后会正常访问到 SCC 中的所有点。
这个算法的好处是,你不需要知道每一条边,只需要能够快速找到当前点的一条对应点未访问过的出边即可。在某些边数很多,不能显式建图的题有用(如果是竞赛图还能把缩点后的链求出来,直接一遍排序即可)。