タイトルによって、要件を完了するための最小数の操作を見つけることができます。もちろん、グラフは常に最小点から分割できますが、これは特に低速です。
したがって、考えを変えて、すべてのポイントの重みを最初に合計することができます。この場合、繰り返しがあることがわかっているので、小さいものから大きいものに変換し、大きいものから小さいものへトラバースします。各ノードに到達すると、現在のノードに接続されたポイントを検索します。接続されたポイントが以前にトラバースされ、それらが同じセット内にない場合、現在の位置でのポイントの重みが繰り返され、減算されてから配置されますそれをコレクションに入れます(そしてコレクションをチェックして完了します)。この方法でトラバーサルを完了した後、残りの重量合計が必要な答えです。
コード:
#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;
}