杂项

Kurskal 重构树

定义

Kurskal 重构树就是在跑 Kurskal 的时候处理出一棵树,这棵树会有一些奇妙的性质。

考虑如何构建。

首先新建 \(n\) 个集合,每个集合前有一个节点,点权为 \(0\)

每一次加边会合并两个集合,我们就可以新建一个点,点权为加入边的边权,同时将两个集合的根节点分别设为新建点的左右儿子。然后我们把这两个集合和新建点合并成一个集合。新建点设为根。

如此,我们就得到了一棵树。这棵树就叫作 Kurskal 重构树。

举个例子:

image

如上这张图的 Kurskal 生成树就为如下这棵树:

image

性质

由于一共进行了 \(n-1\) 次加边,即新加入 \(n-1\) 个点,所以 Kurskal 生成树的点数就为 \(2n-1\)

不难发现,原图中两个点之间的所有 简单路径上的最大值 的最小值 = 最小生成树上两个点路径上的最大值 = Kurskal 生成树上两个点的 LCA 的点权。

参考代码

for (int i = 1; i <= n; i++) fa[i] = i, rt[i] = i;
tot = n;
for (int i = 1; i <= m; i++) {
	int x = getfa(e[i].x), y = getfa(e[i].y);
	if (x != y) {
		fa[y] = x;
		tot++;
		G[tot].push_back(rt[x]);
		G[tot].push_back(rt[y]);
		rt[x] = rt[y] = tot;
		val[tot] = e[i].w;
	}
}

ST 表 + 欧拉序求 LCA

概述

这个东西呢,比较帅。 \(O(n\log n)\) 预处理,\(O(1)\) 查询。

首先明确 ST 表的功能:\(O(n\log n)\) 预处理, \(O(1)\) 查询徐建最值。

其次,说一下欧拉序。就是在每次经过这个节点的时候都把他加入到序列末尾。比如这么一棵树,他的欧拉序就是 \(1,2,3,2,4,5,4,6,4,2,1,7,1\)。不难发现,\(n\) 个点的树的欧拉序长度为 \(2n-1\)

image

除了欧拉序之外,我们还需要一个 dfs 序(就是长度为 \(n\) 的那个)。需要注意,欧拉序和 dfs 序公用一个时间戳。

然后呢,对于两个点 \(x\)\(y\),我们找到他们在欧拉序中第一次出现的位置(可以拿 dfs 序直接找到),那么这两个位置之间深度最小的点即为 \(x\)\(y\) 的 lca。

接下来随便怎么搞一搞就好了。

参考代码

int dfn[N], id[2 * N], tot;
void dfs(int x) {
	id[++tot] = x;
	dfn[x] = tot;
	for (int y : G[x]) {
		dfs(y);
		id[++tot] = x;
	}
}
int Log2[2 * N], f[2 * N][18];
void init() {
	for (int i = 2; i <= tot; i++) Log2[i] = Log2[i / 2] + 1;
	for (int i = 1; i <= tot; i++) f[i][0] = dfn[id[i]];
	for (int j = 1; j <= Log2[tot]; j++) {
		for (int i = 1; i + (1 << j) - 1 <= tot; i++) {
			f[i][j] = min(f[i][j - 1], f[i + (1 << j - 1)][j - 1]);
		}
	}
}
int getlca(int x, int y) {
	if (dfn[x] > dfn[y]) swap(x, y);
	int s = Log2[dfn[y] - dfn[x] + 1];
	return id[min(f[dfn[x]][s], f[dfn[y] - (1 << s) + 1][s])];
}
posted @ 2025-02-06 11:37  Zctf1088  阅读(30)  评论(0)    收藏  举报