最小生成树与 Kruskal 重构树
基本概念
对于有限可重集 \(S\),称 \(S\) 中最大的数为 \(S\) 的瓶颈.(视语境不同也可指最小的数.)有限可重集之间可以定义大小关系:将元素分别从小到大排序,得到一个权值序列,按序列的字典序比较可重集的大小.
对于无向带权连通图 \(G\),若 \(G\) 的一个无环连通子图 \(T\) 包含 \(G\) 的所有顶点,则称 \(T\) 为 \(G\) 的一个生成树. \(T\) 的边权总和称作生成树的大小,\(T\) 的边权瓶颈称作生成树的瓶颈,\(T\) 的边权从小到大排序后得到生成树的边权序列. 对某个图 \(G\),所有生成树中最小的称作最小生成树,瓶颈最小的称作最小瓶颈生成树,边权序列字典序最小的称作最小字典序生成树.
一般地,对于无向带权图 \(G\)(不一定连通),也可定义相应的生成森林.
最小字典序生成树
对于图 \(G\),设生成树 \(T\) 的边权序列为 \(\{t_i\}\),最小字典序生成树 \(M\) 的边权序列为 \(\{m_i\}\),则 \(\{m_i\}\) 的字典序小于等于 \(\{t_i\}\) 的字典序. 事实上我们有更强的结论:\(\{m_i\}\) 的每个位置都小于等于 \(\{t_i\}\)!于是能够推出,对任意 \(k\),\(M\) 让第 \(k\) 大的边取到了最小. 特别地,\(M\) 让最大的边取到了最小,也就是说,最小字典序生成树都是最小瓶颈生成树. 另外,既然每条边都取到了最小,最小字典序生成树也是最小生成树.(同时最小生成树都是最小字典序生成树,因此这两种生成树等价;这也说明所有最小生成树的边权序列都相同.)
下面我们来证明这个结论. 用反证法,设某个 \(i\) 能使 \(m_i>t_i\). 现在在 \(M\) 中只保留前 \(i-1\) 条边(记为 \(M'\)),这会形成 \(n-i+1\) 个连通块(\(n\) 为点数),将图 \(G\) 划分成 \(n-i+1\) 个点集. 在 \(T\) 的 \(n-1\) 条边中,至少要有 \(n-i\) 条边是“跨点集”的(不然 \(T\) 不连通了),根据抽屉原理,至少有一条边是 \(t_1,t_2,\cdots,t_i\) 之一(因为其他的边只有 \(n-1-i\) 条),设它是 \(t_j\ (1\le j\le i)\). 现在把 \(t_j\) 加进 \(M\),这会在 \(M\) 中形成一个环. 由于 \(t_j\) 是“跨点集”的(即 \(t_j\) 的两个端点在 \(M'\) 中不连通),环上至少有一条边不属于 \(M'\),不妨设它是 \(m_k\),这里我们有 \(i\le k\le n-1\). 因为 \(\{t_i\}\) 和 \(\{m_i\}\) 都是升序,有 \(t_j\le t_i<m_i\le m_k\). 于是只要把 \(m_k\) 删掉,就把 \(M\) 改造成了一棵字典序更小的生成树,这就导出了矛盾.
Kruskal 算法
给定图 \(G\),我们用贪心法求解 \(G\) 的最小字典序生成树. 首先把 \(G\) 中所有边权从小到大排序,依次将这些边加入生成树中. 对于某一条边 \((u,v,w)\),若加入之后不形成环(即加入前 \(u\) 与 \(v\) 不连通)则加入,否则跳过. 可用并查集维护连通性. 时间复杂度 \(O(m\log m)\),其中 \(m\) 为边数.
这样贪心有一点小问题:如果存在相等的边权,它们加入的先后顺序如何确定呢?事实上,相等的边权无论按什么顺序加入,最后都会得到一棵最小字典序生成树. 这很容易证明:考虑我们即将加入一批边权为 \(c\) 的边. 加入前的图 \(G\) 可能已经连了一些边,我们把每个连通块都看作一个点;考察全体 \(c\),忽略在连通块内部的边(缩点后相当于自环),我们先把其他 \(c\) 都加入 \(G\),然后取新 \(G\) 的一个生成森林即可. 显然,取的边数是确定的. 另外,加入后新形成的连通块会在下一批边加入前缩掉,所以取哪个生成森林对之后的连通性没有影响.
这段分析是重要的. 我们在矩阵树定理的相关问题中还会见到它.
这便是最小生成树的 Kruskal 算法.
最小瓶颈路
对于图 \(G\) 上的一条路径,可定义它的瓶颈为边权的瓶颈(本文中约定为最大值). 对于点 \(u\) 和 \(v\),\(u\) 到 \(v\) 的最小瓶颈路是两点间所有路径中瓶颈最小的. 最小瓶颈路可能有多个,我们通常只希望求出一条来. 下面我们证明:设 \(M\) 是 \(G\) 的一个最小生成树,则 \(u\) 到 \(v\) 在 \(M\) 上的简单路径一定是 \(G\) 上的最小瓶颈路.
用反证法,假设 \(u\) 到 \(v\) 在 \(M\) 上的路径不是最小瓶颈路. 设最小瓶颈为 \(c\). 我们把路径上最大的那条边 \(w\)(根据假设,\(w>c\))删去,这将 \(M\) 分成两个连通块,一个包含 \(u\),另一个包含 \(v\). 考虑一条真正的最小瓶颈路,它从 \(u\) 走到 \(v\) 必定会跨过两个连通块,设跨过的这条边为 \(w'\),则有 \(w'\le c<w\). 现在把 \(w'\) 加回 \(M\),\(M\) 就变成了更小的生成树,这就导出了矛盾.
因此,给定图 \(G\),先求一棵最小生成树,再用树上倍增或树剖求路径最值,这就求出了最小瓶颈. 时间复杂度 \(O(m\log m+q\log n)\),其中 \(m\) 为边数,\(n\) 为点数,\(q\) 为询问次数.
Kruskal 重构树
参考 cqy 课件,在此表示感谢.
Kruskal 算法是对生成树性质的充分利用. 有时候我们需要记录算法过程中的某些信息,这需要Kruskal 重构树.
在算法进行过程中,Kruskal 重构树始终是一个森林(若 \(G\) 连通则最后会变成一棵树). 首先建立 \(n\) 个叶子结点,对应图 \(G\) 的 \(n\) 个结点. 接下来进行 Kruskal 算法,按边权升序枚举 \(G\) 的边. 当一条边 \((u,v,w)\) 被跳过时不做操作,否则查出 \(u\)、\(v\) 两点所在树的根 \(r_u\)、\(r_v\)(此时一定有 \(r_u\ne r_v\)),新建权值为 \(w\) 的结点 \(p\),将 \(r_u\) 和 \(r_v\) 设为 \(p\) 的儿子.
这样做的正确性显然,因为在任意时刻,点 \(u\) 和 \(v\) 具有相同的树根当且仅当 \(u\) 和 \(v\) 在 Kruskal 算法中是否已经连通. 事实上,这棵重构树本身类似于一个有边权的并查集(而且可查历史版本). 只需略微调整 Kruskal 算法中的并查集,每次不把 \(u\) 和 \(v\) 直接合并而是把它们都与新根 \(p\) 合并(注意合并方向!),就能快速查出点所在树的根.
下面给出 Kruskal 重构树的一些性质.
- 重构树是一棵有 \(n\) 个叶子的完满二叉树(非叶子节点恰有两个儿子).
注意:整棵重构树有 \(2n-1\) 个结点. 数组要开到 \(2\) 倍.
- 非叶子结点的点权满足大根堆性质(父亲的权值大于等于左右儿子).
- 最小生成树上 \(u\) 到 \(v\) 的简单路径对应于重构树上叶子 \(u\) 到叶子 \(v\) 的简单路径.
这两条性质给出一种新的求最小瓶颈路的方法:在重构树上求两点的最近公共祖先,它的权值即为最小瓶颈.
例题
待续……