题目大意:有一张$n$个点$m$条边的图,要求对于每条边求出包含这条边的最小生成树
题解:先求出最小生成树,发现加入一条不在最小生成树上的边,就会出现一个环,那么把这个环上除这条边外权值最大的一条边删去就是对于这条边的最小生成树,可以倍增求
卡点:倍增结尾处理错
C++ Code:
#include <cstdio>
#include <algorithm>
#define maxn 200010
int head[maxn], cnt;
struct Edge {
int to, nxt, w;
} e[maxn << 1];
inline void add(int a, int b, int c) {
e[++cnt] = (Edge) {b, head[a], c}; head[a] = cnt;
e[++cnt] = (Edge) {a, head[b], c}; head[b] = cnt;
}
int l[maxn], r[maxn], w[maxn], rnk[maxn];
bool yes[maxn];
inline bool cmp(int a, int b) {return w[a] < w[b];}
int f[maxn];
int find(int x) {return (x == f[x] ? x : (f[x] = find(f[x])));}
int n, m;
long long ans;
#define M 18
int fa[M][maxn], S[M][maxn], dep[maxn];
inline int max(int a, int b) {return a > b ? a : b;}
void dfs(int u) {
for (int i = 1; i < M; i++) {
fa[i][u] = fa[i - 1][fa[i - 1][u]];
S[i][u] = max(S[i - 1][u], S[i - 1][fa[i - 1][u]]);
}
for (int i = head[u]; i; i = e[i].nxt) {
int v = e[i].to;
if (v != fa[0][u]) {
fa[0][v] = u;
dep[v] = dep[u] + 1;
S[0][v] = e[i].w;
dfs(v);
}
}
}
inline ask(int x, int y) {
int res = 0;
if (dep[x] < dep[y]) std::swap(x, y);
for (int i = dep[x] - dep[y]; i; i &= i - 1) res = max(res, S[__builtin_ctz(i)][x]), x = fa[__builtin_ctz(i)][x];
if (x == y) return res;
for (int i = M - 1; ~i; i--) if (fa[i][x] != fa[i][y]) {
res = max(res, max(S[i][x], S[i][y]));
x = fa[i][x], y = fa[i][y];
}
return max(res, max(S[0][x], S[0][y]));
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 0; i < m; i++) {
scanf("%d%d%d", l + i, r + i, w + i);
rnk[i] = i;
}
for (int i = 1; i <= n; i++) f[i] = i;
std::sort(rnk, rnk + m, cmp);
for (int j = 0, i, lastnum = n - 1; j < m && lastnum; j++) {
i = rnk[j];
int u = find(l[i]), v = find(r[i]);
if (u != v) {
add(l[i], r[i], w[i]);
yes[i] = true;
f[u] = v;
lastnum--;
ans += w[i];
}
}
dep[1] = 1;
dfs(1);
for (int i = 0; i < m; i++) {
if (yes[i]) printf("%lld\n", ans);
else printf("%lld\n", ans - ask(l[i], r[i]) + w[i]);
}
return 0;
}