Boruvka 求最小生成树
Boruvka 算法的思想基于每个点的最短邻边一定在最小生成树上。算法流程是每轮对每个连通块找到一条连向另一连通块的最短边,然后合并两端点。因为每轮连通块数量至少减半,所以一共会进行 \(O(\log n)\) 轮。
相比 Kruskal 和 Prim,Boruvka 在求稠密图的最小生成树上具有优势,前提是能快速找到一个点连向与它不同连通块的最短边。
1. CF1648E Air Reform
我们先考虑如何求给定两点在原图的最短路。套路地,建出 Kruskal 重构树,最短路就是两点 \(\text{LCA}\) 的权值。
对于在补图的最短路,我们希望如法炮制,建出补图的 Kruskal 重构树。但是由于边数太多无法跑 Kruskal。不妨退一步,建出补图的最小生成树,那么两点的最短路就是这两点在最小生成树上的简单路径的最大边权。
求一个稠密图的最小生成树,可以考虑 Boruvka 算法。其思想基于每个点的最短邻边一定在最小生成树上,流程是每轮对每个连通块找到一条连向另一连通块的最短边,然后合并两端点。因为每轮连通块数量至少减半,所以一共会进行 \(O(\log n)\) 轮。
于是现在我们考虑对每个点 \(u\),求出它连出去的最小出边,并且边的另一端点不和 \(u\) 在同一连通块。我们不妨倍增找到 \(u\) 在 Kruskal 重构树上的最浅祖先,使得它包含的所有叶子不是都和 \(u\) 在同一连通块。
考虑把 Kruskal 重构树拍平到序列上,那么每个点的子树管辖 dfn 序在 \([l_i, r_i]\) 中的叶子。设 \(f_i\) 为 \(i\) 所在连通块的代表元,\(g_i\) 为 dfn 序为 \(i\) 的点,\(d_i\) 为最大的 \(j\) 使得 \(\forall k \in [j, i], f_{g_k} = f_{g_i}\),也就是说 \(g_{d_i - 1}\) 是 \(i\) 在 dfn 序上往左第一个与 \(g_i\) 不在同一连通块的点。
考虑倍增到 \(u\) 的一个祖先 \(v\) 后,如何判定 \(v\) 子树管辖的叶子不是都和 \(u\) 在同一连通块,并且如果这个条件满足,还要找到这样的一个叶子。如果 \(g_{r_v}\) 不和 \(u\) 在同一连通块,那么 \(g_{r_v}\) 即为所求。否则,dfn 序在 \([d_{r_v}, r_v]\) 中的叶子都和 \(u\) 在同一连通块,若 \(d_{r_v} > l_v\),那么 \(g_{d_{r_v} - 1}\) 即为所求,否则 \(v\) 不满足条件。
但是有个问题,因为我们求的是补图的最小生成树,所以找到的边不能是原图上有的边。考虑判定 \(v\) 是否满足条件时,对于所有 \(u\) 在原图上连出去的边 \((u, w)\),\(l_w\) 把 \([l_v, r_v]\) 分成了 \(O(deg_u)\) 个小区间。因为 \(O(\sum deg_u) = O(m)\),所以我们可以暴力找到 \(O(deg_u)\) 个小区间,再按照上面的方法判定即可。
时间复杂度 \(O(m (\log^2 n + \log m))\)。
2. CF888G Xor-MST
考虑 Boruvka。因为 \(a_x \oplus a_y\) 取到最小值的 \(y\) 不能和 \(x\) 在同一连通块,所以我们分连通块处理,使用 01Trie,求出不包含它的一个前缀和后缀的异或最小值即可。
时间复杂度是 \(O(n \log n \log V)\),但是被卡常了。按照讨论区说的对 \(a\) 排序,常数优化显著,不知道为什么。
3. CODE FESTIVAL 2017 Final J Tree MST
求完全图的最小生成树,立刻想到 Boruvka。
于是剩下的任务是,对于每个点 \(y\),找到当前和它不在同一连通块的点 \(y\) 的 \(F(x, y) = w_y + dis_{x, y}\) 的最小值。
如果没有 \(x, y\) 所在连通块不同的限制,可以很轻易地换根 dp 完成。先自下而上求出 \(y\) 在子树内的 \(F(x, y)\) 最小值,再自上而下求出 \(y\) 在子树外 \(F(x, y)\) 最小值。
加上了这个限制,我们除了求每个 \(x\) 的 \(F(x, y)\) 最小值和它对应的 \(y\),还要求次小值和它对应的 \(y\)。需要注意我们强制规定最小值和次小值对应的 \(y\) 当前所在连通块不同。这样如果 \(x\) 跟最小值的 \(y\) 在同一连通块,就可以让次小值递补。
时间复杂度 \(O(n \log n)\)。
4. GDCPC2023 L Classic Problem
对于一个点 \(x\),若 \(\exists i, u_i = x \lor v_i = x\),则称 \(x\) 为特殊点,否则为一般点。
首先发现,对于极长的一段 \([l, r]\) 满足 \(l \sim r\) 均为一般点,那么可以连边 \((l, l + 1), (l + 1, l + 2), \ldots, (r - 1, r)\),然后把 \([l, r]\) 缩成一个连续点。因为这些点通过别的点与外界连通显然不优。
对于一个特殊点 \(x\),我们把它变成区间为 \([x, x]\) 的连续点。然后把所有连续点按区间左端点排序后重编号。
然后现在相当于我们有至多 \(4m + 1\) 个点的完全图,有一些给定边,若对于之间不存在给定边的点对 \(u, v\ (u < v)\),它们之间的边权是 \(l_v - r_u\)。求这个完全图的最小生成树。
考虑 Boruvka 算法,其流程是每轮对每个连通块找到一条连向另一连通块的最短边,然后合并两端点。
考虑模拟流程。我们首先考虑给定边,然后考虑其他边。前者是容易的。至于后者,我们希望找到 \(u\) 左右侧最接近 \(u\) 且和 \(u\) 不在同一连通块且和 \(u\) 之间没有给定边的点 \(v\)。于是我们每次先处理出 \(pre_u\) 和 \(nxt_u\) 表示 \(u\) 左侧(或右侧)最接近 \(u\) 且和 \(u\) 不在同一连通块的点,然后枚举 \(u\),暴力找 \(v\),以左侧为例,就是若 \(u, v\) 之间存在给定边就 \(v \gets v - 1\),否则 \(v \gets pre_v\)。因为给定边数量是 \(O(m)\) 的,所以这部分复杂度是对的。
若使用 set
存给定边,时间复杂度为 \(O(m \log^2 m)\)。