大意:
给出一个无向带权图,每个点都有权值,现在可以每次在一个连通块中选择权值最小的点并将所有点减去这个权值,该点变为0,花费为这个最小权值,问将所有点都变为0最小的花费
考虑一个简单的图:
假设
,那么第一次花费
,
分别变为
,然后二者是相互独立的,也就是说和
相连的边都会变成独立的连通块,最后总的花费是
。这个过程如果写程序的话,每次连通块都在变化,又要寻找每个块的最小值,似乎程序无法实现…
正解:看到连通块应该想到并查集,正着不好做就考虑一下反着来,每次使用优先队列维护找到最大的,然后加上这个权值并标记这个点已经访问,相当于原来的连通块都加上了这个最大权值,然后找到下一个最大值,如果下一个最大值和之前已经确定的最值有联系的话,实际上是多加了当前权值,那么就是遍历所有邻点找到已访问的,使用并查集连接连通块,然后更新答案
#include <bits/stdc++.h>
#include <unordered_map>
#include <unordered_set>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define ins insert
#define Vector Point
#define lowbit(x) (x&(-x))
#define mkp(x,y) make_pair(x,y)
#define mem(a,x) memset(a,x,sizeof a);
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<double,double> pdd;
const double eps=1e-8;
const double pi=acos(-1.0);
const int inf=0x3f3f3f3f;
const double dinf=1e300;
const ll INF=1e18;
const int Mod=1e9+7;
const int maxn=2e5+10;
int f[maxn];
bool vis[maxn];
vector<int> G[maxn];
priority_queue<pii> q;
int Find(int x){
return f[x]==x?x:f[x]=Find(f[x]);
}
int main(){
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
ios_base::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t,n,m;
cin>>t;
while(t--){
cin>>n>>m;
for(int i=1,w;i<=n;i++){
f[i]=i,G[i].clear();
cin>>w;
q.push({w,i});
}
memset(vis,0,sizeof vis);
for(int i=0,u,v;i<m;i++){
cin>>u>>v;
G[u].pb(v);
G[v].pb(u);
}
ll ans=0;
while(!q.empty()){
pii u=q.top();
q.pop();
vis[u.se]=1;
ans+=u.fi;
for(auto i: G[u.se]) if(vis[i]){
int fi=Find(i);
if(u.se!=fi){
f[fi]=u.se;
ans-=u.fi;
}
}
}
cout<<ans<<"\n";
}
return 0;
}