专栏文章

题解:P14080 [GESP202509 八级] 最小生成树

P14080题解参与者 2已保存评论 2

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
2 条
当前快照
1 份
快照标识符
@minqgp9j
此快照首次捕获于
2025/12/02 06:41
3 个月前
此快照最后确认于
2025/12/02 06:41
3 个月前
查看原文
真的有人能在考场上默写出这题吗?
考虑对于边的位置分类讨论,对于非树边,答案即为原图最小生成树的边权和。
对于树边,会将最小生成树分为两个连通块,答案即为所有连接两个连通块的边的最小值。
考虑这个如何维护,不妨把题目转化一下。注意到断掉的边刚好在连接边的路径上。于是我们可以枚举所有非树边,然后更新路径上的最小值。
这个可以用倍增表维护。
最后增加的答案即为连上的边减去断掉的边,注意特判 1-1
CPP
#include <bits/stdc++.h>
#define int long long
using namespace std;

using i32 = int32_t;
using i64 = int64_t;
using ui32 = uint32_t;
using ui64 = uint64_t;

namespace strdp {
	const int N = 1e5;
	int n, m, cnt, q;
	int fa[N + 5];
	struct node { int u, v, w, idx; } a[N + 5];
	bool flg[N + 5];
	struct node2 { int v, w; };
	bool cmp(node x, node y) { return x.w < y.w; }
	vector<node2> g[100005];
	int fd(int x) { if (fa[x] == x) return x; return fa[x] = fd(fa[x]); }
	void jn(int x, int y) { fa[fd(x)] = fd(y); }
	int kruskal() {
		int res = 0;
		for (int i = 1; i <= m; i++) {
			int u = fd(a[i].u), v = fd(a[i].v);
			if (u != v) {
				flg[i] = 1;
				g[a[i].u].push_back({a[i].v, a[i].w});
				g[a[i].v].push_back({a[i].u, a[i].w});
				fa[u] = v; res += a[i].w;
				if (++cnt == n - 1) return res;
			}
		}
	}
	int f[N + 5][21], dep[N + 5], res[N + 5][21];
	bool vis[N + 5];
	void dfs1(int x, int fa) {
		vis[x] = 1; dep[x] = dep[fa] + 1;
		f[x][0] = fa; res[x][0] = LLONG_MAX;
		for (int i = 1; i <= 20; i++) {
			f[x][i] = f[f[x][i - 1]][i - 1];
			res[x][i] = LLONG_MAX;
		}
		for (auto u : g[x]) {
			if (u.v != fa) dfs1(u.v, x);
		}
	}
	int lca(int x, int y) {
		if (dep[x] < dep[y]) swap(x, y);
		for (int i = 20; i >= 0; i--) {
			if (dep[f[x][i]] >= dep[y]) {
				x = f[x][i];
			}
		}
		if (x == y) return x;
		for (int i = 20; i >= 0; i--) {
			if (f[x][i] != f[y][i]) {
				x = f[x][i];
				y = f[y][i];
			}
		}
		return f[x][0];
	}
	void update(int u, int v, int w) {
		int l = lca(u, v);
		for (int i = u; dep[i] > dep[l]; ) {
			int j = f[i][0];
			res[i][0] = min(res[i][0], w);
			i = j;
		}
		for (int i = v; dep[i] > dep[l]; ) {
			int j = f[i][0];
			res[i][0] = min(res[i][0], w);
			i = j;
		}
	}
	int ans[N + 5];
	void main() {
		cin >> n >> m;
		for (int i = 1; i <= n; i++) fa[i] = i;
		for (int i = 1; i <= m; i++) {
			int u, v, w;
			cin >> u >> v >> w;
			a[i] = {u, v, w, i};
		}
		sort(a + 1, a + m + 1, cmp);
		int s = kruskal();
		dfs1(1, 0);
		for (int i = 1; i <= m; i++) if (!flg[i]) update(a[i].u, a[i].v, a[i].w);
		for (int i = 20; i >= 1; i--) {
			for (int j = 1; j <= n; j++) {
				res[j][i - 1] = min(res[j][i - 1], res[j][i]);
				if (f[j][i - 1] != i) res[f[j][i - 1]][i - 1] = min(res[f[j][i - 1]][i - 1], res[j][i]);
			}
		}
		for (int i = 1; i <= m; i++) {
			if (!flg[i]) ans[a[i].idx] = s;
			else {
				if (dep[a[i].u] < dep[a[i].v]) swap(a[i].u, a[i].v);
				if (res[a[i].u][0] == LLONG_MAX) ans[a[i].idx] = -1;
				else ans[a[i].idx] = s - a[i].w + res[a[i].u][0];
			}
		}
		for (int i = 1; i <= m; i++) cout << ans[i] << "\n";
	}
}

i32 main() {

	int t = 1;
	// cin >> t;

	while (t--) strdp::main();
	return 0;
}

评论

2 条评论,欢迎与作者交流。

正在加载评论...