[题解] CF609E Minimum spanning tree for each edge


前言

题目链接:洛谷

题目链接:CodeForces

题意

给你 \(n\) 个点, \(m\) 条边,如果对于一个最小生成树中要求必须包括第 \(i(1<=i<=m)\) 条边,那么最小生成树的权值总和最小是多少。

思路

首先求出该图的最小生成树。最小生成树的边的答案就是最小生成树的权值和。记录下来这个权值和,记为 \(ans\)

考虑必须包含不在生成树中的边,强行加入了最小生成树对于答案的影响。

就相当于现将这条边加入生成树中,然后再跑 kruskal ,需要找到被并查集查到相同的父亲的那条边,且这条边在最小生成树内。

可以发现,直接在生成树中加入这条边形成的环,都有可能受这条边的影响。

由于按照 kruskal 的思想,边是被排了序的,影响到的边,只有权值最大的一条。

于是可以维护不带修改的树上路径最大值。用LCA或者树剖+ST表/线段树都可以维护。

Code

开了long long 就不会死。

#include 
#include 
#include 
using namespace std;
#define LL long long
const int MAXN = 2e5 + 5;
struct Edge {
	int To, Dis, Next;
};
int head[MAXN], tot = 1;
Edge edge[MAXN << 1];
void Addedge(int u, int v, int w) {
	edge[++tot].Next = head[u], edge[tot].To = v, edge[tot].Dis = w, head[u] = tot;
	edge[++tot].Next = head[v], edge[tot].To = u, edge[tot].Dis = w, head[v] = tot;
}
struct Edge1 {
	int u, v, w, id;
	friend bool operator < (Edge1 x, Edge1 y) {
		return x.w < y.w;
	}
};
Edge1 e[MAXN];
int dp[MAXN][40], maxn[MAXN][40];
int fa[MAXN], dep[MAXN], n, m;
bool flag[MAXN];
LL ans;
int Find(int x) {
	return fa[x] == x ? x : (fa[x] = Find(fa[x]));
}
void Union(int x, int y) {
	fa[Find(x)] = fa[Find(y)];
}
int Maxpast(int x, int y) {
	int res = 0;
	if (x == y) return res;
	if (dep[x] < dep[y])
		swap(x, y);
	for (int i = 30; i >= 0; i--) {
		if (dep[x] - (1 << i) >= dep[y]) {
			res = max(res, maxn[x][i]);
			x = dp[x][i];
		}
	}
	if (x == y) return res;
	for (int i = 30; i >= 0; i--) {
		if (dp[x][i] != dp[y][i]) {
			res = max(res, max(maxn[x][i], maxn[y][i]));
			x = dp[x][i];
			y = dp[y][i];
		}
	}
	res = max(res, max(maxn[x][0], maxn[y][0]));
	return res;
}
void dfs(int u, int fa) {
	dp[u][0] = fa;
	dep[u] = dep[fa] + 1;
	for (int i = head[u]; i; i = edge[i].Next) {
		int v = edge[i].To;
		if (v == fa) continue;
		dfs(v, u);
		maxn[v][0] = edge[i].Dis;
	}
}
bool cmp(Edge1 x, Edge1 y) {
	return x.id < y.id;
}
int main() {
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= m; i++) {
		scanf("%d %d %d", &e[i].u, &e[i].v, &e[i].w);
		e[i].id = i;
	}
	for (int i = 1; i <= n; i++)
		fa[i] = i;
	sort(e + 1, e + 1 + m);
	for (int i = 1; i <= m; i++) {
		int u = Find(e[i].u), v = Find(e[i].v);
		if (u == v) continue;
		Union(u, v);
		ans += e[i].w;
		flag[e[i].id] = 1;
		Addedge(e[i].u, e[i].v, e[i].w);
	}
	dfs(1, 0);
	for (int j = 1; j <= 30; j++) {
		for (int i = 1; i <= n; i++) {
			dp[i][j] = dp[dp[i][j - 1]][j - 1];
			maxn[i][j] = max(maxn[i][j - 1], maxn[dp[i][j - 1]][j - 1]);
		}
	}
	sort(e + 1, e + 1 + m, cmp);
	for (int i = 1; i <= m; i++) {
		LL tmp = 0;
		if (flag[i])
			tmp = ans;
		else
			tmp = ans - Maxpast(e[i].u, e[i].v) + e[i].w;
		printf("%lld\n", tmp);
	}
	return 0;
}