Kruskal 重构树

创建时间:2025-07-28


Kruskal重构树的过程

Kruskal重构树是Kruskal生成树的进阶版,它的大致思想与Kruskal生成树相同,都是先将边按权值排序,然后优先合并次序靠前的边,只要该边连接的两个点还不在同一个联通块就选择该边。但不同于Kruskal生成树的是,Kruskal生成树每次直接选择边并将两个联通块合并,Kruskal重构树是先创建一个新的虚拟节点,将原边所连接的两个点所在的树根看做虚拟节点的儿子,原边的权值看做虚拟节点的点权。很容易发现,在Kruskal重构树之后,一共有 \(2n-1\) 个节点。虽然增加了 \(n-1\) 个节点,但将边权转化为了点权,这就多了很多可利用的性质。它的模版代码如下:


inline int find(int x) {
	return f[x] == x ? x : f[x] = find(f[x]);
}

void Kruskal() {
	sort(edge + 1, edge + m + 1);
	for (int i = 1; i <= 2 * n - 1; i++)
		f[i] = i;
	for (int i = 1, j = n; i <= m; i++) {
		int x = edge[i].u, y = edge[i].v;
		if (find(x) != find(y)) {
			w[++j] = edge[i].w;
			lc[j] = f[x], rc[j] = f[y];
			f[f[x]] = f[f[y]] = j;
		}
	}
}

Krukal重构树的性质

以下默认排序是按边权小的优先排序的,如果排序方式不同,只需将下述内容中大/小取反即可。

1,在不考虑叶子结点的情况下,Kruskal重构树是一个二叉大根堆。
首先Kruskal重构树肯定是一颗二叉树(废话)。根据Kruskal重构树的过程,每个非叶子节点都是从两棵树合并而来的,而那两棵树的根在该点前出现,由Kruskal生成树的过程知道它们的权值肯定优于该点,故Kruskal重构树是一个父节点劣于子节点的大根堆。结合二者得Kruskal重构树是一个二叉大根堆。

2,在Kruskal重构树这棵二叉树中,编号为 \(1 \sim n\) 的节点均为叶子结点。
这是显然的,因为非叶子节点都是从两棵较小的树合并而来的,而 \(1 \sim n\) 显然不是通过合并得来的,它们是原图中的点。

3,原图中两点的所有路径中边权最大值的最小值等于Kruskal重构树中两点LCA的点权。
记原图中两点的所有路径中边权最大值的最小值为 \(ans\)。由Kruskal生成树的性质知:\(ans\) 等于Kruskal生成树中两点路径边权最大值的最小值。Kruskal重构树将边权转化为了点权,所以对应到Kruskal重构树中有 \(ans\) 等于Kruskal重构树中两点路径点权最大值的最小值,又有Kruskal重构树是大根堆,即任意节点的点权都大于其子孙的点权。故 \(ans\) 等于Kruskal重构树中两点的公共祖先的点权的最小值,也就有 \(ans\) 等于Kruskal重构树中两点LCA的点权。

有了这三个性质,得以使Kruskal重构树经常与树上倍增、dfs序+数据结构等树上算法一起应用,可以轻松处理原来复杂的图中问题。

posted @ 2026-06-02 16:14  xubaichuan  阅读(10)  评论(0)    收藏  举报