Tarjan 算法求割点
图的割点
割点可以理解为是图中这样一种点:去掉它之后,原来的图割成了更多个子图,也即,这个图的极大连通分量数增加了。
图的割点可以用暴力的方法求得:遍历每一个点,删除并计算新的连通分量数。当然这个方法就跟它看起来那样愚蠢。
求图的割点可以使用 Tarjan 算法。
Tarjan 算法的分析
Tarjan 算法是类似 DFS 的算法:它对每一个点计算两个值,low 和 dfn。
其中 dfn 是这个点的访问顺序,类似树的深度;而 low 是这个点所连接到的最高的点的 dfn(当然不包括它的父亲)。
通过这两个数据,我们怎么判断一个点是不是割点呢?
low 是关键。
low 的实质是某个点所能连到的最高祖先。对于一个点 A,如果它的一个孩子 B 能够连接到比 A 更高的点,即 low[b] < dfn[a] 那么明显删除了 A 对 B 没有任何影响,因为 B 可以通过 low 中存储的祖先来与图联通。
但是如果 low[b] >= dfn[a],也就是说 B 不能连接到比 A 更高的点,那么把 A 删除,明显会让 B 和整个图断开连接。所以,只要存在这样的孩子点,A 就是一个割点
(B:A,至少对我来说,你是不可或缺的)。
Tarjan 算法的实现
自然是 DFS。以 P3388 【模板】割点(割顶) 为例。
存图用邻接链表即可。之后对每一个没有访问过的点 i (vis[i] == false),我们进行一次 Tarjan(因为题目中提到本图不一定联通)。
DFS 中先设置 vis 和 dfn,然后将 low 初值设为 dfn。然后对每一个孩子进行 DFS,同时若孩子能连到比自己更高的点,便更新 low 与孩子的 low 相同。
之后我们判断是否是割点:如果 low[t] >= dfn[u] 并且这个点没有被找到过(因为我们需要进行多次 Tarjan,防止重复计算)我们就更新割点数量。
这里和普通 DFS 有一点不同:当孩子已经被访问过时,我们也需要对其处理,以更新 low 值。
同时还需要注意一点:
开始 Tarjan 的点需要进行特判,但也十分简单。我们知道,只要它有两个以上孩子,那么它一定是割点(因为起始点的入度是为零的)
以上就是 Tarjan 算法求割点的具体步骤。代码可参考 OI Wiki。