学完后,你应能在线性时间找出无向图中的桥(bridge),按删桥后的连通块得到边双连通分量(edge-biconnected component, e-BCC),并把原图压成桥树解决直径、补边和最优定向问题。
前置:无向 DFS、树的直径、上一节的 dfn/low 思想。
if (v == parent) continue,遇到重边会怎样?逐条删边再做连通性检查是 $O(E(V+E))$。DFS 树已经把“删一条树边会隔开哪一块”固定下来:对树边 $u-v$,只需问 $v$ 的整棵子树能否通过别的边回到 $u$ 或更早祖先。
定义 low[v] 为 $v$ 子树通过树边和任意多条非父边能返回祖先或子树内节点的最小 dfn。完成子树后:
$$ (u,v)\text{ 是桥}\iff low[v]>dfn[u]. $$
删除全部桥后,每个剩余连通块内部任意两点之间都有两条边不相交路径;把每块缩成点、把桥保留,连通原图得到一棵树,非连通原图得到森林。若缩点后仍有环,环上的“桥”就能绕行,矛盾。
递归模板很短,但本节例题达到 $4\times10^5$ 个点,课程参考实现采用显式栈模拟 DFS:
parent_edge[v] 记录把 $v$ 带入 DFS 树的边编号;cursor[u] 记录邻接表扫描位置;low[v] 合并给父亲并判桥;dfn[to] 更新,指向后代时这个值更大,不会错误降低 low。每点、每条邻接记录只进出常数次,时间 $O(V+E)$,空间 $O(V+E)$。
给定连通简单无向图,$2\le n\le3\times10^5$,$n-1\le m\le3\times10^5$。任选起终点 $s,t$,所有从 $s$ 到 $t$ 都必须经过的边可放置 Boss。求最多能放多少个。
非桥位于某个环上,可以绕开;桥删去后两侧断开,若 $s,t$ 分居两侧就必经。缩点后每条树边对应一座桥,因此指定 $s,t$ 的 Boss 数等于其分量在桥树中的距离。树上最大两点距离是直径,用两次 BFS 求得。
桥判定、缩点、建树、两次 BFS 都是线性,总复杂度 $O(n+m)$。
bridges = find_bridges_with_edge_ids(graph)
component = flood_fill_while_ignoring(bridges)
bridge_tree = contract_components_and_keep_bridges()
a = farthest_vertex(bridge_tree, arbitrary_root)
b, distance = farthest_vertex(bridge_tree, a)
output distance
完整 GNU C++17 参考实现见附件:CodeForces-1000E.cpp
若边有权且目标是必经桥权值和最大,桥树直径改为带权直径;非桥权值仍不计入,因为它们都可绕开。
连通无向图有 $1\le F\le5000$ 个点、$F-1\le R\le10^4$ 条边,允许重边。至少增加多少条边,才能使任意两点之间存在两条边不相交路线?允许新增平行边。
桥树有 $L$ 个叶子。每个叶子只靠一座桥连接外界,要消除这座桥,至少有一条新增边跨出该叶子,因此至少需要 $\lceil L/2\rceil$ 条。把叶子两两配对并连接各自分量内任一点,新增边与树上路径组成环,覆盖路径上的桥;奇数个叶子时最后一叶与已使用叶适当连接,仍只需 $(L+1)/2$ 条。下界可达。
若缩点后只有一个点,叶数为 0,答案为 0。复杂度 $O(F+R)$。
bridges = find_bridges_with_edge_ids(graph)
component = flood_fill_while_ignoring(bridges)
degree = degrees in the contracted bridge tree
leaves = count(degree == 1)
output ceil(leaves / 2)
完整 GNU C++17 参考实现见附件:POJ-3177.cpp
允许重边使“按父顶点跳边”的写法必错。若原图不连通,还必须先连接不同树,答案不能对每棵桥树简单独立取整后相加。
给定连通简单无向图,$2\le n,m\le4\times10^5$。给每条边定向,令 $r_i$ 为从 $i$ 可达的城市数,最大化 $\min r_i$,并按输入顺序输出定向。
任何桥树定向都有汇点,汇边双分量内的点只能到达该分量内部,因此最小可达数不超过最大边双分量大小 $S$。
选大小为 $S$ 的分量作根,所有桥由子分量指向父分量。每个无桥分量内部按 DFS 树边向下、返祖边向上定向;无桥条件保证每条树边的子树都有返祖通路回到祖先,从而分量强连通。任意一个非根分量的点能先在分量内到达朝根桥的起点,逐层进入根,到达根内全部 $S$ 个点;根内任意点至少能到达根内自身与其他同分量点共 $S$ 个点(再经指出的桥向其子分量扩展可达更多点,但不影响下界)。因此 $\min r_i \ge S$,与上界相等,达到最优。
find bridges and edge-biconnected components
root = component with maximum number of original vertices
inside each component: orient DFS-tree edges down and back edges up
build the bridge tree rooted at root
orient every bridge from child component toward parent component
output size(root) and all orientations in input order
完整 GNU C++17 参考实现见附件:CodeForces-732F.cpp
本题的关键是“任意树定向必有汇点”给出上界,而不是先猜构造。若目标改成最大化平均可达数,把桥全部朝一个根定向未必最优,上界结构也会改变。
We Need More Bosses 为什么是桥树直径?Redundant Paths 的叶子下界如何由一条新增边达到两个端点?Tourist Reform 的上界和达到上界的构造分别是什么?先对拍桥判定:随机删一条边,比较连通块数量是否增加。再做桥树直径与叶子计数。Tourist Reform 可先只口述上界和定向方向,再实现;调试时分别验证每条原边恰输出一次、非桥分量强连通、所有桥指向所选根。