Kruskal重构树 学习笔记
我们回顾一下最小与最大生成树的性质:
对于一张图的最小生成树,原图中任意两个节点中任意一条路径的边权最大值的最小值为生成树中节点路径间边权的最大值。最大生成树则相反,原图中任意两个节点中任意一条路径的边权最小值的最大值为生成树中节点路径间边权的最小值。
以下以最小生成树为例,最大生成树则同理。
回顾一下
- 是一颗二叉树;
- 若原本需要生成的是最小生成树,则重构树是一个大根堆,最大生成树则相反;
- 原图中任意两点在重构树上的
的点权即为该两点在原图中经过的路径边权最大值的最小值,即在最小生成树中经过的路径边权最大值。这意味着对于一个权为 的节点,其子树中的任意两个节点都可以在原图(或最小生成树)中通过权都小于等于 的边到达。
结合图理解:
该图的最小生成树是:
该图的
节点旁边的数值为该点的点权。
例题:P4197 Peaks
题目大意:给定一张
思路:容易想到对原图建立
P4197 AC Code:
#include <bits/stdc++.h>
#define mid (l + r >> 1)
using namespace std;
struct E {
int u, v, w;
const bool operator<(const E &rhs) const {
return w < rhs.w;
}
};
int n, m, u, v, w, gr, cnt, macnt = 1, tot, q, f[200005], fa[200005][20], h[100005], p[200005], b[100005], r[100005], rt[200005], sum[40000005], ls[40000005], rs[40000005];
E e[500005];
unordered_map<int, int> ma;
int fgr(int u, int x) {
for (int i = 19; ~i && p[fa[u][0]] <= x; --i)
if (p[fa[u][i]] <= x)
u = fa[u][i];
return u;
}
int find(int x) {
return x == f[x]? x: f[x] = find(f[x]);
}
void merge(int l, int r, int &x, int y, int z) {
if (!y) { x = z; return; }
if (!z) { x = y; return; }
sum[x = ++tot] = sum[y] + sum[z];
if (l < r) {
merge(l, mid, ls[x], ls[y], ls[z]);
merge(mid + 1, r, rs[x], rs[y], rs[z]);
}
}
void ExKruskal() {
cnt = n;
sort(e + 1, e + m + 1);
for (int i = 1; i <= m; ++i)
if ((u = find(e[i].u)) != (v = find(e[i].v))) {
f[u] = f[v] = ++cnt;
f[cnt] = cnt;
p[cnt] = e[i].w;
fa[u][0] = fa[v][0] = cnt;
merge(1, macnt, rt[cnt], rt[u], rt[v]);
}
}
void add(int l, int r, int &x, int tar) {
x = ++tot;
if (l == r) {
++sum[x];
return;
}
if (tar <= mid) add(l, mid, ls[x], tar);
else add(mid + 1, r, rs[x], tar);
sum[x] = sum[ls[x]] + sum[rs[x]];
}
int query(int l, int r, int x, int k) {
if (l == r) return l;
if (sum[ls[x]] >= k)
return query(l, mid, ls[x], k);
else
return query(mid + 1, r, rs[x], k - sum[ls[x]]);
}
int main() {
p[0] = 2e9;
scanf("%d %d %d", &n, &m, &q);
for (int i = 1; i <= n; ++i) {
scanf("%d", &h[i]);
f[i] = i, b[i] = h[i];
}
sort(b + 1, b + n + 1);
r[1] = b[1];
ma[b[1]] = 1;
for (int i = 2; i <= n; ++i)
if (b[i] != b[i - 1]) {
r[++macnt] = b[i];
ma[b[i]] = macnt;
}
for (int i = 1; i <= n; ++i)
add(1, macnt, rt[i], ma[h[i]]);
for (int i = 1; i <= m; ++i) {
scanf("%d %d %d", &u, &v, &w);
e[i].u = u, e[i].v = v, e[i].w = w;
}
ExKruskal();
for (int i = 1; i < 20; ++i)
for (int j = 1; j <= cnt; ++j)
fa[j][i] = fa[fa[j][i - 1]][i - 1];
while (q--) {
scanf("%d %d %d", &u, &v, &w);
gr = fgr(u, v);
if (sum[rt[gr]] < w)
puts("-1");
else
printf("%d\n", r[query(1, macnt, rt[gr], sum[rt[gr]] - w + 1)]);
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!