题目让我们找出最小的操作次数 来完成要求 我们当然可以 从最小的点开始一直把这个图分裂下去,但是这样进行是特别慢的。
所以我们可以换一下思维,先把所有的点的权值加起来,我们知道这样的话是会有重复的,然后再把从小到大转换为从大到小的遍历,走到每一个节点时,找出与当前节点相连的点,如果相连的点之前已经被遍历过并且他们不再一个集合中,那么代表这个当前位置上的点的权值是重复的,把它减掉,然后再把他们放到一个集合中去(并查集完成即可)。这样完成遍历之后 ,剩下的权值和就是我们要的答案。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 2e6+7;
vector<int>G[MAXN];
int n,m;
ll w[MAXN];
int pre[MAXN];
int vis[MAXN];
struct node
{
ll w;
int id;
}point[MAXN];
bool cmp(node a,node b){
return a.w > b.w;
}
int Find(int x)
{
if(pre[x] == x) return x;
else return pre[x] = Find(pre[x]);
}
int main()
{
int t;
scanf("%d",&t);
while(t--){
ll ans = 0;
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;i ++){
pre[i] = i;
vis[i] = 0;
G[i].clear();
}
for(int i = 1;i <= n;i ++){
scanf("%lld",&w[i]);
point[i].w = w[i],point[i].id = i;
ans += point[i].w;
}
while(m--){
int u,v;
scanf("%d%d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
//for(int i = 1;i <= n;i ++){
//for(int j = 0;j < G[i].size();j ++) printf("---%d to is %d\n",i,G[i][j]);
//}
sort(point+1,point+1+n,cmp);
//for(int i = 1;i <= n;i ++) printf("%d %d\n",point[i].id,point[i].w);
for(int i = 1;i <= n;i ++){
for(auto to : G[point[i].id]){
if(vis[to]){
int a = Find(point[i].id),b = Find(to);
if(a != b){
pre[b] = a;
ans -= w[point[i].id];
}
}
vis[point[i].id] = 1;
}
}
printf("%lld\n",ans);
}
return 0;
}