// A Simple and Efficient Algorithm for Finding Minimum Spanning Tree Replacement Edges // David A. Bader // Paul Burkhardt #include using namespace std; struct UF { vector e; UF() {} UF(int n) { e.resize(n); iota(e.begin(), e.end(), 0); } bool sameSet(int a, int b) { return find(a) == find(b); } int find(int x) { return e[x] == x ? x : e[x] = find(e[x]); } void join(int a, int b) { if (a != b) e[b] = a; } }; int n, m, cnt = 0, best = 0; vector>> adj; vector par, pre, post, parwt; vector> edges, non_mst; UF paths; void dfs(int u, int p, int w) { par[u] = p; parwt[u] = w; pre[u] = cnt++; for (auto [v, w] : adj[u]) if (v != p) dfs(v, u, w); post[u] = cnt++; } void path_label(int s, int t, int w) { if (pre[s] < pre[t] && pre[t] < post[s]) return; int plan, k1, k2;; if (pre[t] < pre[s] && pre[s] < post[t]) plan = 0, k1 = pre[t], k2 = pre[s]; else { if (pre[s] < pre[t]) plan = 1, k1 = post[s], k2 = pre[t]; else plan = 0, k1 = post[t], k2 = pre[s]; } int v = s; while (k1 < k2) { if (paths.find(v) == v) { paths.join(par[v], v); best = max(best, w - parwt[v]); } v = paths.find(v); if (plan == 0) k2 = pre[v]; if (plan == 1) k1 = post[v]; } } int main() { cin.tie(0); ios::sync_with_stdio(0); cin >> n >> m; adj.resize(n); par.resize(n); pre.resize(n); post.resize(n); parwt.resize(n); paths = UF(n); for (int i = 0; i < m; i++) { int a, b, w; cin >> a >> b >> w; a--; b--; assert(a < b); edges.emplace_back(a, b, w); } sort(edges.begin(), edges.end(), [](auto a, auto b) { return std::get<2>(a) < std::get<2>(b); }); UF uf(n); long long mst_wt = 0; for (auto [a, b, w] : edges) { if (!uf.sameSet(a, b)) { mst_wt += w; adj[a].emplace_back(b, w); adj[b].emplace_back(a, w); uf.join(uf.find(a), uf.find(b)); } else non_mst.emplace_back(a, b, w); } dfs(0, 0, 0); for (auto [a, b, w] : non_mst) path_label(a, b, w), path_label(b, a, w); cout << mst_wt+best << endl; }