先并查集处理出每个集合,和每个集合最小的点的下标
然后把每个点权值从大到排序,砍k条边其实相当于加m - k条边,每次加一条边可以减少一个点的权值(除了每个集合的最小点),这样从大到小把能删的尽量贪心删掉即可
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long ll;
const int N = 1000005;
const int INF = 0x3f3f3f3f;
int t, n, m, k, w[N], parent[N], Min[N], id[N];
int find(int x) {
if (x == parent[x]) return x;
return parent[x] = find(parent[x]);
}
bool cmp(int a, int b) {
return w[a] < w[b];
}
int main() {
int cas = 0;
scanf("%d", &t);
while (t--) {
ll ans = 0;
scanf("%d%d%d", &n, &m, &k);
for (int i = 1; i <= n; i++) {
Min[i] = INF;
parent[i] = i;
Min[i] = i;
scanf("%d", &w[i]);
id[i] = i;
ans += w[i];
}
int u, v;
for (int i = 0; i < m; i++) {
scanf("%d%d", &u, &v);
int pu = find(u);
int pv = find(v);
if (pu != pv) {
if (w[Min[pu]] < w[Min[pv]])
Min[pv] = Min[pu];
parent[pu] = pv;
}
}
k = m - k;
sort(id + 1, id + n + 1, cmp);
for (int i = n; i >= 1; i--) if (k && Min[find(id[i])] != id[i]) ans -= w[id[i]], k--;
printf("Case #%d: %lld\n", ++cas, ans);
}
return 0;
}