[题解] CF609E Minimum spanning tree for each edge
前言
题意
给你 \(n\) 个点, \(m\) 条边,如果对于一个最小生成树中要求必须包括第 \(i(1<=i<=m)\) 条边,那么最小生成树的权值总和最小是多少。
思路
首先求出该图的最小生成树。最小生成树的边的答案就是最小生成树的权值和。记录下来这个权值和,记为 \(ans\) 。
考虑必须包含不在生成树中的边,强行加入了最小生成树对于答案的影响。
就相当于现将这条边加入生成树中,然后再跑 kruskal ,需要找到被并查集查到相同的父亲的那条边,且这条边在最小生成树内。
可以发现,直接在生成树中加入这条边形成的环,都有可能受这条边的影响。
由于按照 kruskal 的思想,边是被排了序的,影响到的边,只有权值最大的一条。
于是可以维护不带修改的树上路径最大值。用LCA或者树剖+ST表/线段树都可以维护。
Code
开了long long 就不会死。
#include <cstdio>
#include <vector>
#include <algorithm>
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;
}