强连通分量、双连通分量以及圆方树。
Robert E. Tarjan(罗伯特·塔扬,1948~),生于美国加州波莫纳,计算机科学家。 Tarjan 发明了很多算法和数据结构。不少他发明的算法都以他的名字命名,以至于有时会让人混淆几种不同的算法。比如求各种连通分量的 Tarjan 算法,求 LCA(Lowest Common Ancestor,最近公共祖先)的 Tarjan 算法。并查集、Splay、Toptree 也是 Tarjan 发明的。 摘自 OI-Wiki
一、强连通分量
0、有向图的 DFS 生成树
(1) DFS 序
$u$ 的 DFS 序即为从某节点(根)开始遍历,首次到达 $u$ 的时间戳。
(2) 四条边
- 树边:DFS 树上的边。
- 返祖边:指向祖先节点的边。
- 前向边:指向子孙节点的边。
- 横叉边:指向的访问过的节点既不是自己的祖先节点也不是自己的子孙。
如下图,黑边为树边,绿边为返祖边,粉边为前向边,红边为横叉边。
1. 基本概念
- 强连通:在有向图 $G$ 中,任意两点联通。
- 强连通分量(SCC):有向图 $G$ 中,极大的强连通子图。对于“极大”,意思是这个子图无法再加入任意一点使其仍为强连通的。
- SCC 的根:遍历这个 SCC 的第一个点
1.5 约定
- $dfn_u$:遍历到 $u$ 的时间戳。
- $low_u$:以 $u$ 为根的子树可以回溯到最早已经在栈中的节点的时间戳。
2. 求法
Trick
一个 SCC 的所有节点必定在其根的子树中。
我们于 dfs 过程中维护一个栈,里面是未处理的节点。
对于 $u$,我们枚举每一条 $u$ 出边 $(u,v)$。
- 若 $v$ 未访问。我们向 $v$ 遍历,并更新 $low_u\gets\min(low_u,low_v)$。
- 若 $v$ 已访问,且在栈内。$low_u\gets\min(low_u,dfn_v)$。
- 若 $v$ 已访问,不在栈内。那么 $v$ 所在 SCC 已经处理完毕,不操作。
值得注意的是,有时在写强连通分量时,我们误将 $low_u\gets\min(low_u,dfn_v)$ 写作 $low_u\gets\min(low_u,low_v)$,虽然运行结果正确。然而这实际上不符合我们推导以及思考过程,在求双连通分量中这样是错误的。因此需要注意这个问题。
对于一个 SCC,有且仅有一个点使得 $low_u=dfn_u$。
简证
存在性:假设 SCC 根节点为 $R$,那么必然有 $dfn_R=low_R$。这是因为如若 $low_R < dfn_R$,那么说明存在点 $v$ 使 $v$ 加入当前 SCC 点集后仍然是一个 SCC,与定义“极大的”矛盾。
唯一性:除根节点外的所有子节点,必有 $low_u< dfn_u$。这是由于所有子节点都可以回溯到时间戳早于它的节点。
我们只需找到这样的点,将这个点以及栈内所有晚于这个点入栈的点弹出,所构成的即为一个 SCC。
|
|
这整个过程的时间复杂度非常优秀,为 $\mathcal O(n+m)$。
3. 例题
P3387 【模板】缩点
模板题。 我们可以发现,在经过缩点操作后的新图是一个有向无环图,故可以在新图上进行 DP 操作。
P2341 [USACO03FALL / HAOI2006] 受欢迎的牛 G
对原图缩点后,原图中属于出度为 $0$ 的那个点的所有点即为答案。
P2812 校园网络【[USACO]Network of Schools加强版】
缩点,第一个答案为新图入度为 $0$ 点的个数,第二个答案为入度为 $0$ 点个数与出度为 $0$ 点个数的最大值。
P4306 [JSOI2010] 连通数
缩点后建反图,拓扑 DP 即可。 另解:用 bitset 优化的传递闭包。
P2272 [ZJOI2007] 最大半连通子图
原图上是存在环的,所以我们先缩点,得到一张 DAG。根据半连通的定义,我们从入度为 $0$ 的点开始 DP。设 $c_i$ 表示 SCC $i$ 所含节点数,$f_i$ 表示到 $i$ 的最大半连通子图节点数,$g_i$ 表示方案数。那么对于边 $(u,v)$,$f_v\gets \max(f_v,f_u+c_v)$,$g_v\gets\begin{cases}g_u,& f_v<f_u+c_v\g_u+g_v,&f_v=f_u+c_i\end{cases}$。
二、双联通分量,割点与桥
0. 无向图的 DFS 生成树
这部分十分简单,图上边分为树边与非树边。顾名思义。
1. 基本概念
- 割点:无向连通图 $G$ 中,若删除 $u$ 使得 $G$ 不再连通,那么 $u$ 就是 $G$ 的一个割点。
- 桥(割边):无向连通图 $G$ 中,若删除边 $e$ 使得 $G$ 不再连通,那么 $e$ 就是 $G$ 的一个桥。
- 边双连通:若无向连通图 $G$ 不存在桥,那么 $G$ 就是边双连通的。
- 点双连通:若无向连通图 $G$ 不存在割点,那么 $G$ 就是点双连通的。
- 边双连通分量:若 $G’$ 是无向图 $G$ 的 极大 的边双联通子图,那么 $G’$ 就是 $G$ 的一个边双连通分量。下文简称为“边双”。
- 点双连通分量:若 $G’$ 是无向图 $G$ 的 极大 的点双联通子图,那么 $G’$ 就是 $G$ 的一个点双连通分量。下文简称为“点双”。
这里的“极大”与强连通分量中含义一致。
1.5 约定
- $dfn_u$:遍历到 $u$ 的时间戳。
- $low_u$:以 $u$ 为根的子树通过非树边可到达的最小时间戳。
- $subtree_u$:以 $u$ 为根的子树。
2. 割点 & 点双联通分量
(1) 求割点
类似的,我们考虑 $dfn$ 与 $low$ 的关系。 对于点 $u$ ,若存在 $v\in subtree_u$ 使得 $low_v\ge dfn_u$,也就是说 $v$ 无法通过非树边到达 $u$ 的祖先,那么 $u$ 即为一个割点。特别的,当 $u$ 为搜索起始点时,若其仅有一个子节点,那么删除 $u$ 并不会增加图的连通子图数,因此并不会成为割点;反之,若其多于一个子节点,那么其必然为割点。
简证
命题:$\exist v\in subtree_u,low_v\ge dfn_u\iff u$ 是割点。若 $v\in subtree_u$,那么 $v$ 是第一次被遍历,且由 $u$ 而来。且对于其到达不在根到 $u$ 路径上的节点的非树边,到达的时间戳必然是大于其本身的。因此当 $low_v\ge dfn_u$ 时,$v$ 必然无法通过非树边到达 $u$ 的祖先。 如若存在这样的 $v$,但 $u$ 不是割点。由于 $v$ 无法通过非树边到达 $u$ 的祖先,故 $v$ 必须通过 $u$ 到达其余点,因此 $u$ 必然为割点。故充分性成立。 如若 $u$ 为割点,假设不存在这样的 $v$:那么所有 $v$ 必然经过 $u$ 到达小于 $low_u$ 的点。然而根据 dfs 的性质,我们发现连接 $u$ 的非树边的 $dfn$ 值是必然大于 $u$ 的,同样的,向上遍历亦如此。故必要性成立。
|
|
(2) 求点双连通分量
由于点双中不存在割点,因此我们类比求割点的做法,就可以推出点双的做法。 同样的,我们维护一个栈。对于 $u$,我们如果枚举到了 $v$ 使得 $low_v\ge dfn_u$,那么 $u$ 即为割点。并不断弹出栈内元素直至栈顶为 $u$,这些部分构成一个点双。值得注意的,对于根节点,如若没有子树,则它就单独构成一个点双;如若有多个子树,那么就是一个割点;如若只有一个子树,那么他就是所在点双的根。
|
|
3. 桥 & 边双连通分量
(1) 桥
我们只需将割点的判定条件改为 $low_v>dfn_u$ 即可。证明与上类似。值得注意的是,若 $(u,v)$ 仅有一条边时为桥,多于一条边时,需要特殊判定。若 $v$ 多次枚举到 $u$,那么不为桥。我们只需加上特判即可。
|
|
(2) 边双连通分量
我们先把所有桥求出,后再次进行 dfs。具体的,我们枚举每个未被枚举的点 $u$,从 $u$ 开始搜索,将所有不经过桥所连点加入当前的边双集合。这样就得到了全体的边双集合。
|
|
实际上也有另一种简便的求法,与求强连通分量的过程十分相似(实际上一致)。
|
|
4. 例题
P8435 【模板】点双连通分量 & P8436 【模板】边双连通分量 & P3388 【模板】割点(割顶)
模板题。
P3225 [HNOI2012] 矿场搭建
我们对每一个点双讨论:
- 若当前点双不存在割点,则这个点双不与外界相连,因此只能从其本身逃出。那么至少需要两个出口(防止其中之一被炸了)。
- 若当前点双存在一个割点,割点可能会被炸,因此我们还需要再这个点双内放置一个出口。
- 若当前点双存在不少于两个割点,那么任意一个点爆炸都可以从其他割点逃出。因此无需放置。
那么做法就十分显然了。设出口数为 $cnt$,方案数为 $ans$,当前点双的点数为 $n$。对于度数为 $0$ 的点双,$cnt\gets cnt+2$,$ans\gets ans\times n\times (n-1)\times\dfrac12$。对于度数为 $1$ 的点双,$cnt\gets cnt+1$,$ans\gets ans\times (n-1)$(根据前面的讨论,割点是不能当作出口的)。对于其他度数的点双,我们无需统计。
P8867 [NOIP2022] 建造军营
首先进行边双缩点,后形成一个树结构,两点之间的连边为桥。考虑树形 DP。
圆方树
仙人掌
一条边至多在一个环内的无向连通图。
构建
对于所有环,建立虚点——「方点」。对于环上点,称为「圆点」。
在新图中,对于不在同一个环中的圆点,继承原图中的连边关系。对于环上的所有圆点,向方点连边。于是新图的所有边分成了「圆圆边」、「圆方边」。
实际上,这是一种「边双建图」的方式。故含「圆圆边」。
特别的,在广义圆方树(即一般图的圆方树)中,采用的是点双建图。仅含圆方边。

关于代码,由于广义圆方树包含这种情况,所以就不特地写仙人掌圆方树的代码了。详细见下文的一般图圆方树代码。
例题
P5236 【模板】静态仙人掌
给出有边权的仙人掌,多次询问点对最短路。
$n,q\le 10000,m\le 20000$。
$w\le 10^5$。
按照上述过程建立圆方树,考虑如何对两点求出距离,即对新图边的赋值问题。
对于圆圆边, 显然按照原图权值分配即可。故只需考虑对于圆方边的刻画。
不妨任意选一圆点为根。由于仙人掌的性质,一个环上的点的深度呈现 $d,d+1,d+1\cdots,d+1$。取深度最小的点,称之为该环的根。每个点到方点的边权就是点到根的最小距离。
由于仙人掌的良好性质,一个环仅有一条返祖边。维护环上点通过返祖边或者不通过返祖边到达根的距离。
在查询时,直接查询到 LCA 的距离之和。若 LCA 为方点,考虑在 LCA 处的最小路径即可。
|
|
P4630 [APIO2018] 铁人两项
给出无向连通图,求 $s\to c\to f$ 为简单路径的有序点对数。
$n\le10^5,m\le2\times10^5$。
这里其实可以讲一个点双的性质。
性质
- 一个点双中 $u,v$ 的简单路径的并集为整个点双。
这是一个十分良好的性质。可以推广出在整个图中,固定 $s,f$ 后,所有合法的 $c$ 为 $s\to f$ 在圆方树上的简单路径上,经过所有点双的并减二(不能选自己)。
下面就是对圆方点赋值问题。由于仅圆方点是交替的,故在路径上(不包括 $s,f$)每个圆点都会在两个方点之中。因此对所有圆点赋值 $-1$,方点赋值为所在点双大小,这样就可以直接求出二者之间的 $c$ 的数量。设点的权为 $w_i$。
然而这个做法是 $\mathcal O(n^2)$ 的,考虑优化。
设 $f(u)$ 表示 $u$ 作为 $c$ 的 $s,f$ 点对个数,$sz_u$ 表示以 $u$ 为根的子树中圆点数量。讨论 $u$ 为方点还是圆点。
- 对于方点,考虑两两子树圆点配对,以及以当前为根的子树与整棵去掉该子树的树上圆点的配对。即 $2w_u\times [\sum\limits_{w,v\in son_u} siz_w\times siz_v+(siz_{rt}-siz_{u})\times siz_u]$。
- 对于圆点,根据上文所言,直接容斥掉重复部分。与上转移式相同。
如果对于圆点的容斥不太清楚,可以考虑每次「配对」实际上是枚举 $s,f$ 的过程。对于每个 $u$ 都确定了互不相同的多个点对。考虑把这个东西看作固定 $s,f$ 求 $c$ 数量,显然由于圆方间隔这个容斥是对的,这就说明一组点对只会被两个方点重复统计。因此统计到圆点直接减掉经过其的所有点对就是对的,这两者转化之间是一一映射的。
P4606 [SDOI2018] 战略游戏
给出无向连通图。多次询问,每次询问给出点集 $S$。求点集内任意两点路径上割点数量之和。
$\vert S\vert\le 2\times10^5$。
把圆方树建出来,问题转化为求任意两点简单路径圆点并集大小。朴素的方法是 $\mathcal O(n^2)$ 的,考虑如何刻画并集这一过程。
发现实际上只要求任意二者之间的 LCA 后求并。考虑 DFS 的遍历过程,对于先后遍历顺序相邻两个关键点,DFS 的过程实际上就是在图上「行走」,且每个点至多被经过两次。将点按照 DFS 序排序后,相邻两点就是 DFS 序的遍历过程。因此,被经历过点的个数就是相邻(首尾算相邻)两点路径和的一半。
计算圆点个数是 $d(u)+d(v)-2d(lca)$,这是因为 $lca$ 会被计算包含,不能被重复计算。
过程是进出两次,故对路径之和取一半。注意首尾 LCA 圆点没被计算,算进去即可。