引理:所有未确定权值的边中,只有一条被确定为 ,其余所有边的权值都被确定为 。
证明:考虑生成树的形态,有两种情况。
-
MST 使用了所有未确定权值的边,那么根据 ,可以得到 。这是因为总是存在 。故可以在 ()的情况下取等。
-
MST 没有使用所有未确定权值的边,那么我们可以令没被用的某条边的权值为 ,其他边的权值都为 ,代价显然是最小的。
我们在所有未确定权值的边上面进行 DFS。仅根据这些未被确定权值的边,这张图会被划分为若干个联通块,于是我们需要使用一些已经确定权值的边来确定 MST。我们将已经确定权值的边划分为 3 中类型:
-
MST 中必须使用的边:按照边权排序后,最小的那些连接两个不同连通块的边。
-
MST 中不可能存在的边:与边权更小的边形成环的边。换言之,不在原图 MSF(最小生成森林)中的边。
-
不属于前两种情况的边。
对于未被确定权值的边,可以划分为两种情况:
-
未被确定权值的边形成了至少一个环:我们可以钦定这个环上的任意一条边为 ,其他为 ,那么使用这个环上的边权为 的边以及第一类已经确定边权的边构造 MST。
-
未被确定权值的边没有形成环。假定我们仅仅使用未确定边权的边和第一类已经确定边权的边构造了一棵 ST,那么任何第三种类型的边可以替换一条未被确定权值的边。这是因为第三种类型的边必然会和第一种类型以及未确定权值的边构成一个环(否则它就是第一类或者第二类了)。如果存在某条第三种类型的边 ,那么我们就可以进行替换。
#include <bits/stdc++.h> using namespace std; const int N = 2e5 + 5; struct edge { int u, v, w; bool operator < (const edge &oth) const { return w < oth.w; } } e[N << 1]; int n, m, x, mn = INT_MAX; long long ans, rem; struct disjoint_sets_union { int fa[N]; int Query(int p) { if(fa[p] == p) return p; return fa[p] = Query(fa[p]); } } dsu; queue<int> que; set<int> unvised, G[N]; bool vis[N], type[N]; void FindBlocks() { for(int i = 1; i <= n; i++) if(!vis[i]) { que.push(i); vis[i] = true; unvised.erase(i); dsu.fa[i] = i; while(!que.empty()) { int u = que.front(); que.pop(); for(set<int>::iterator it = unvised.begin(); it != unvised.end();) { int v = *it; it++; if(G[u].count(v)) continue; dsu.fa[v] = u; unvised.erase(v); vis[v] = true; que.push(v); rem--; } } } } int main() { ios::sync_with_stdio(false); cin >> n >> m; rem = 1LL * n * (n - 1) / 2 - m; for(int i = 1; i <= n; i++) unvised.insert(i); for(int i = 1; i <= m; i++) { cin >> e[i].u >> e[i].v >> e[i].w; G[e[i].u].insert(e[i].v); G[e[i].v].insert(e[i].u); x ^= e[i].w; } sort(e + 1, e + m + 1); FindBlocks(); for(int i = 1; i <= m; i++) { int fau = dsu.Query(e[i].u), fav = dsu.Query(e[i].v); if(fau != fav) { dsu.fa[fau] = fav; ans += e[i].w; type[i] = 1; // type 1 } } if(rem > 0) return printf("%lld\n", ans) && 0; for(int i = 1; i <= n; i++) dsu.fa[i] = i; for(int i = 1; i <= m; i++) { int fau = dsu.Query(e[i].u), fav = dsu.Query(e[i].v); if(fau == fav) continue; // type 2 dsu.fa[fau] = fav; if(!type[i]) { mn = e[i].w; break; } // type 3 } printf("%lld\n", ans + min(x, mn)); return 0; }