最小生成树
Contents
介绍
最小生成树就是给定一张边有权值的图,求一个生成树使得边权和最小。
最小生成树有着以下几个性质:
- 最小生成树不唯一,次小生成树可以通过枚举非树边
,然后替换最小生成树上 这条链上最大边来实现。 - 所有的最小生成树中,相同权值的边的数量一定相同。
- 所有的最小生成树中,对于任意的权值
,如果把所有权值 的边单独拿出来,那么构成的图的连通性均相同。
证明:
- 如果不相同,那么这违背了第一条。因为拥有相同权值的次小生成树一定是通过替换一个相同权值的边实现的。(这一条其实也可以直接通过第三条结论得出)。
- 如果有两种不同的连通性
,那么必然存在一对点 使得 在 联通,而在 中不联通。根据 kruskal,这说明我们可以在 中想办法将 连在一起(因为这样的边一定存在于 当中)。这不可能发生,因为所有权值 的都被拿出来了。
常用的算法 Prim 和 Kruskal 就不再赘述了。
Prim 的思想主要是维护一个联通块,然后逐渐往这个块上加新的点。
Kruskal 的思想是将所有边按照边权 sort 一下,然后用并查集维护联通性防止环的产生。
其实还有一个比较冷门的算法:Boruvka 算法
Boruvka 算法
Boruvka 算法比较适合处理拥有特殊性质的 完全图。比如给定一种计算两点之间边权的方式,然后求最小生成树之类的。
算法总共有
每一轮开始:
- 对于每一个连通块
,我们都找出它与其他连通块的最小边 。 - 对于每个联通块
对应的最小边 ,尝试连接 和 ,如果连接成功,就将连通块合并。
直到只剩下一个联通块。
一般考察这个算法的时候,主要的难点都在于对于每一个联通块,找到它与其他联通块的最小边。
然后因为每次合并,最坏都可以将连通块数量减半,所以最多只有
例1 洛谷P2619 [国家集训队]Tree I
题意
给定一个
给定非负整数
其中,
数据保证有解。
题解
wqs二分(虽然我并不知道这是什么神奇的算法)。
这个算法针对的是 恰好选
具体证明和相关例题以后再学习。
wqs二分的主要思想是给这些 恰好选
然后根据原算法跑出来的结果(选取了多少个),然后用二分调整这个额外权值。
所以对于本题,就是给每个白色边添加一个权值,然后跑最小生成树,如果跑出来的最小生成树拥有
那什么时候更新答案呢?
我们应当在最小生成树跑出
首先,我们不确定这个二分的过程是否会出现:额外权值等于
为什么有可能出现呢?
因为最小生成树有可能不唯一!
不过,既然数据保证了一定有解,我们不妨在所有答案中都尽可能的多选白边,那么对于真正的答案对应的额外权值
所以只要在最小生成树跑出
代码
|
|
例2 CF888G Xor-MST
题意
给定
连接
求这个图的最小生成树权值。
其中,
法一 Boruvka算法
Boruvka 算法。
为什么它可做呢?因为它的合并操作只有
而每一轮操作需要找所有联通块向其他联通块连边的最小边。
如果我们正在处理联通块
- 先将联通块
内的所有元素从 01-Trie 中删除。 - 然后对于联通块内的每一个元素
,都找 01-Trie 内 XOR 起来最小的那个元素。 - 这说明这个联通块的最小边找到了,我们再把块内所有元素 插入回 01-Trie(保证这个 01-Trie 里面维护的始终是所有的点权)。
找到所有联通块
时间复杂度
不过这题有个问题,如果有的点权值重复了怎么办?
因为我们在 01-Trie 中寻找到的是一个 XOR 起来最小的点权,但这个点权对应的点可能不唯一。
思考后可以发现,我们先把所有相同的点权全都连起来就好了,毕竟它们之间的边权为
然后所有点权相同的点一定在一个联通块内了,我们用一个 map
把一个点权 map 到它们之中的任意一个点即可。
法一 代码
|
|
法二 01-Trie 子树合并
另外一种做法更加优雅,并且它充分利用了 01-Trie 的特点。
首先我们把所有的
然后我们有几个性质:
- 每个叶子节点都是原数组里的
。 - 拥有
个 child 的节点恰好有 个。 - 两个数字
若位于两个叶子节点 ,设 ,那么它们 XOR 起来的值可以忽略掉 上面的部分,只用考虑 子树内的部分即可。设 所在的高度(叶子的高度为 )为 ,则 对答案的贡献至少为(1<<m)
。
这些性质说明了什么?首先 拥有
并且因为深度越深的节点带来的贡献越小,所以我们应该先合并深度较深的节点。
那么找到这些节点的话,其实只要在 01-Trie 上跑一个 DFS 即可。
当我们到节点
- 只有 左child,那么就接着 DFS 左child。
- 只有 右child,那么就接着 DFS 右child。
- 有 左右child,那么就 DFS 左child 和 右child,然后给答案加上
(1<<m)
,最后加上 的最小值,其中 在左子树内, 在右子树内。
最后剩下的问题就在于,如何求:
想想并查集的启发式合并,对于较小的那个子树,我们枚举子树内的每一个元素
那就有两个问题:
- 如何遍历子树内的每一个元素?
- 如何在一个子树内查询 XOR 最小值?
回答第一个问题:我们一开始把所有的
回答第二个问题:和普通查询一样,只不过改一下在 01—Trie 内的起点即可,同时维护一下当前所在的深度。
法二 代码
|
|
例3 洛谷P4180 [BJWC2010]严格次小生成树
题意
给定一个无向联通图,求其严格次小生成树的权值。
定义严格次小生成树为:一个权值第二小的生成树,且权值严格大于最小生成树的权值。
其中,
题解
次小生成树有两种:
- 非严格次小生成树(权值不一定严格大于最小生成树)
- 严格次小生成树
方法都是一样的,先求出最小生成树,然后枚举每一条 不在 最小生成树上的边
树上链的最大值可以用倍增或者树剖。
这种方法只能求出非严格次小生成树。
对于严格次小生成树,只要我们维护链的最大值和次大值(保证次大值严格小于最大值)即可。
代码
|
|
例4 CF160D Edges in MST
题意
给定一个
对于每一条边,都回答它属于以下的哪种情况:
- 一定在所有 MST(最小生成树)上。
- 一定在至少一个 MST 上。
- 不可能在任何 MST 上。
其中,
数据保证无自环和重边。
题解
先求出一棵 MST。
然后对于每个非树边
那么和非严格次小生成树的求法一样,只要求一下
对于每个树边,要么为
那么其实只要判断是否存在一条非树边,能够将它替换掉即可。
所以在处理每个非树边
那么路径上的询问,还有赋值,都可以用树剖解决,注意一下维护的是链而不是点,所以要特别处理一下
注意点
一开始求 MST 的时候不需要建双向边,因为整个边的数组被 sort 了,所以后面处理一个边是否是树边的时候会比较麻烦。
代码
|
|
例5 CF891C Envy
题意
给定一个
现在给定
对于每个询问,我们需要回答这些边是否能存在于同一个最小生成树当中。
其中,
并且保证所有询问中,询问的边数的总和不超过
题解
由最小生成树的性质第二条和第三条,我们可以发现不同权值的边之间不会互相影响。
所以我们考虑按照权值分开来处理。
换而言之,我们不再按照每个询问来回答,而是把每个询问中的权值相同的边都拿出来分别处理。这样,问题就变成了:
给定一些权值相同(均为
)的边,判断这些边是否能存在于同一个 MST 中?
根据 MST 性质的第三条,我们只需要处理出最小生成树中 权值严格小于 unions()
即可。处理完当前询问,就用可撤销并查集退回。
所以,我们只要把每个询问中所有权值相同的边都拿出来,然后从小到大开始跑 kruskal,跑的过程中回答当前权值的询问即可。
代码
|
|
参考链接
- Boruvka算法:https://luckyglass.github.io/2019/19Oct31stArt1/