Tarjan 算法 (图连通性)
1. 割边和割点
首先我们 dfs 一遍构造出 dfs 树并排出 dfn 序. 显然这棵树没有横叉边.
考虑割边的形成条件. 显然割边只能是树边, 因为非树边会和对应的树上的路径组成环.
考虑边 \((u,v)\), 其中 \(v\) 是 \(u\) 的儿子. 因为没有横叉边, 我们只需要保证从 \(v\) 及其子树 (记作 \(T(v)\) ) 不能通过非树边 (一步) 到达 \(u\) 及其祖先即可.
使用 dfn 序来进行刻画, 自然引出 low 的定义:
对点集 \(S\), 定义它的非树边邻域 (名字随便起的) \(U(S)=S\cup\{u|\exist v\in S: (u,v)\in E-E_T\}\)
其中 \(E\) 是边集, \(E_T\) 是树边集合.
则 \(\text{low}(u)=\min_{v\in U(T(u))}\text{dfn}(v)\), 即邻域内所有点的 dfn 的最小值.
有了 low 的定义, 以上性质即 \(\text{low}(v)>\text{dfn}(u)\). 具体实现只需要求出 low 再在树边上比较就行了.
low 本身也很好求, 根据定义, 只需初始设定 \(\text{low}(u)=\text{dfn}(u)\), 对于树边 \((u,v)\) 有 \(\text{low}(u)\gets \min\{\text{low}(u),\text{low}(v)\}\), 对于非树边 \((u,v)\) 有 \(\text{low}(u)\gets \min\{\text{low}(u),\text{dfn}(v)\}\).
割点也是类似的. 对于点 \(u\), 若存在树上的儿子 \(v\) 使 \(\text{dfn}(u)\leqslant\text{low}(v)\), 则 \(v\) 为割点.
特别地, 如果 \(u\) 为搜索树的根, 此时还需判断相邻子树之间的连通性. 不过因为没有横叉边, 这是简单的, 只需数出 \(u\) 树上儿子的个数, 儿子个数大于等于 \(2\) 则 \(u\) 为割点.
2. BCC
边双连通分量直接把割边删了再 dfs 一遍完事.
找点双也比较简单. 只需要根据 dfs 的顺序将结点入栈, 遇到割点就把割点以上的点弹出, 它们与割点构成了一个点双. 需要注意的是, 割点会在多个点双中出现, 因此不能弹出; 若回溯到根, 不论根是否为割点, 都按照前面的方法处理.
(之后会放边双点双的代码)