CF609E Minimum spanning tree for each edge
原来觉得是一个LCT,感觉自己瞬间傻掉……
考虑到先做一个最小生成树求出做最小生成树的代价$ans$,顺便标记一下树边和非树边,把边按照输入$id$排序回去之后扫,如果扫到一条树边,那么此时的答案就是$ans$,如果扫到一条非树边,那么相当于一条边强制连上之后再切去环里的一条边使这个基环树重新变成一棵树,那么贪心一下,肯定要切掉最大的边,而这个断开一个口的环其实就是树上的一条路径,这个过程只要倍增就可以维护。
时间复杂度$O(nlogn)$。
Code:
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; const int N = 2e5 + 5; const int Lg = 20; int n, m, dep[N], fa[N][Lg], tot = 0, head[N], ufs[N]; ll ans = 0LL, maxn[N][Lg]; struct Edge { int to, nxt; ll val; } e[N << 1]; inline void add(int from, int to, int val) { e[++tot].to = to; e[tot].val = val; e[tot].nxt = head[from]; head[from] = tot; } struct Pathway { int u, v, id; ll val; bool inTree; } path[N]; bool cmpv(const Pathway &x, const Pathway &y) { return x.val < y.val; } bool cmpi(const Pathway &x, const Pathway &y) { return x.id < y.id; } template <typename T> inline void read(T &X) { X = 0; char ch = 0; T op = 1; for(; ch > '9'|| ch < '0'; ch = getchar()) if(ch == '-') op = -1; for(; ch >= '0' && ch <= '9'; ch = getchar()) X = (X << 3) + (X << 1) + ch - 48; X *= op; } inline ll max(ll x, ll y) { return x > y ? x : y; } inline void chkMax(ll &x, ll y) { if(y > x) x = y; } inline void init() { for(int i = 1; i <= n; i++) ufs[i] = i; } int find(int x) { return ufs[x] == x ? x : ufs[x] = find(ufs[x]); } inline void kruskal() { init(); sort(path + 1, path + 1 + m, cmpv); for(int cnt = 0, i = 1; i <= m; i++) { int u = find(path[i].u), v = find(path[i].v); if(u == v) continue; cnt++, ufs[u] = v, path[i].inTree = 1, ans += path[i].val; add(path[i].u, path[i].v, path[i].val), add(path[i].v, path[i].u, path[i].val); if(cnt >= n - 1) break; } } void dfs(int x, int fat, int depth, ll nowDis) { fa[x][0] = fat, dep[x] = depth, maxn[x][0] = nowDis; for(int i = 1; i <= 18; i++) { fa[x][i] = fa[fa[x][i - 1]][i - 1]; maxn[x][i] = max(maxn[fa[x][i - 1]][i - 1], maxn[x][i - 1]); } for(int i = head[x]; i; i = e[i].nxt) { int y = e[i].to; if(y == fat) continue; dfs(y, x, depth + 1, e[i].val); } } inline void swap(int &x, int &y) { int t = x; x = y; y = t; } inline ll getMax(int x, int y) { if(dep[x] < dep[y]) swap(x, y); ll res = 0LL; for(int i = 18; i >= 0; i--) if(dep[fa[x][i]] >= dep[y]) chkMax(res, maxn[x][i]), x = fa[x][i]; if(x == y) return res; for(int i = 18; i >= 0; i--) if(fa[x][i] != fa[y][i]) { chkMax(res, maxn[x][i]); chkMax(res, maxn[y][i]); x = fa[x][i], y = fa[y][i]; } return max(res, max(maxn[x][0], maxn[y][0])); } inline void solve() { sort(path + 1, path + 1 + m, cmpi); /* for(int i = 1; i <= m; i++) printf("%d %d %lld %d\n", path[i].u, path[i].v, path[i].val, path[i].inTree); printf("\n%lld\n", ans); */ for(int i = 1; i <= m; i++) { if(path[i].inTree) printf("%lld\n", ans); else printf("%lld\n", ans - getMax(path[i].u, path[i].v) + path[i].val); } } int main() { read(n), read(m); for(int i = 1; i <= m; i++) { read(path[i].u), read(path[i].v), read(path[i].val); path[i].id = i, path[i].inTree = 0; } kruskal(); dfs(1, 0, 1, 0LL); solve(); return 0; }