连通性+ 1
早在普及组的时候,我们就学会了:
-
DFS(BFS)搜连通块
-
并查集在加边的情况下动态维护连通块(支持离线处理删边)
现在,我问你:
-
我删去一个点/边,判断剩下的图存在原本某两个连通的点现在不连通?
-
我随机删去一条边,判断剩下的图中某两个点是否一定连通?
-
我随机给你一些点,判断其中两两是否互相可达(有向图上)?
第三个问题不是今天这篇文章所讲的范畴,但是其他问题确实可以讲一讲。
我删去一个点/边,判断剩下的图存在原本某两个连通的点现在不连通?
点
形式化定义:有 \(G = (V, E), d \in V\),判断:
\(\exists x, y, x \ne d \land y \ne d \land (\exists v = (v_1, v_2, \cdots, v_k), v_1 = x \land v_k =y \land (\forall 1 \le i < k, (v_i, v_{i + 1}) \in E)) \land (\nexists v = (v_1, v_2, \cdots, v_k), v_1 = x \land v_k =y \land (\forall 1 \le i < k, (v_i, v_{i + 1}) \in E) \land (\forall 1 \le i \le k, v_i \ne d))\),如果成立则称 \(d\) 为割点。
这个问题可以使用 Tarjan 求解。
Tarjan(谐音太监,虽然其正确中文名为塔杨)通过两个数组来求解:\(dfn_i\) 表示 \(i\) 的 DFS 序,\(low_i\) 表示在不经过 \(i\) 在 DFS 树上的父亲所能走到的 \(dfn\) 最小的节点。
如果对于 \(u\) 有一个 \(u\) 的儿子 \(v\) 满足 \(low_v \ge dfn_u\),也就是不能回到祖先,那么就可以确定 \(u\) 是割点了...吗?
根节点的每一个儿子必然满足条件,所以我们要特判:
根是割点当且仅当根在树上有至少两个儿子。
伪代码:
function() Tarjan(x, fa, rt) does
(int)c(0)
dfn[x] = low[x] = ++cnt, vis[x] = 1
for(each i \in adj[x]) does
when(!vis[i]) then
c++
Tarjan(i, x, rt)
low[x] sets(\min) low[i]
when(x \ne rt \and low[i] \ge dfn[x]) then
satis[x] = 1
back on track
and when(i \ne fa) then
low[x] sets(\min) dfn[i]
back on track
back on track
when(x \eq rt \and c \gt 1) then
satis[x] = 1
back on track
back on track
边
形式化定义:有 \(G = (V, E), d \in E\),判断:
\(\exists x, y, (\exists v = (v_1, v_2, \cdots, v_k), v_1 = x \land v_k =y \land (\forall 1 \le i < k, (v_i, v_{i + 1}) \in E)) \land (\nexists v = (v_1, v_2, \cdots, v_k), v_1 = x \land v_k =y \land (\forall 1 \le i < k, (v_i, v_{i + 1}) \in E \backslash \{d\}))\)
跟点的情况很像,具体看伪代码:
function() Tarjan(x, fa) does
dfn[x] = low[x] = ++cnt, vis[x] = 1
for(each i \in adj[x]) does
when(!vis[i]) then
Tarjan(i, x, rt)
low[x] sets(\min) low[i]
(note)(没有根的特殊情况)
when(low[i] \ge dfn[x]) then
satis[i] = 1 (note)(实际上是孩子)
back on track
and when(i \ne fa) then
low[x] sets(\min) dfn[i]
back on track
back on track
(note)(没有根的特殊情况)
back on track
我随机删去一条边,判断剩下的图中某两个点是否一定连通?
很简单:
按照上面的方式处理出桥,断掉桥,条件满足当且仅当之后的图中两个点仍然连通。
附录:Tarjan 模板
function() Tarjan(x, fa) does
dfn[x] = low[x] = ++cnt, vis[x] = 1
for(each i \in adj[x]) does
when(!vis[i]) then
Tarjan(i, x)
low[x] sets(\min) low[i]
and when(i \ne fa) then
low[x] sets(\min) dfn[i]
back on track
back on track
back on track