题意:给出一个由n个点m条边构成的森林,每个点有个权值val[i],额外加一条边(u,v)的花费是val[u] + val[v],且u、v只能被用到一次,添加一些边使得图连通,求最小花费。
思路:先找连通块的个数x,那么需要(x-1)条边使这些块连通,因为不能重复选点,所以需要2(x-1)个点使其连通,要生成一棵树,则至少要有2(x-1)个点。先在每个连通块中找一个最小点,这样一共找了x个点,再从剩余(n-x)个点中找 [2(x-1) - x] 个最小点。对这2(x-1)个点的权求和。特判一开始就是一棵树的情况。
#include <bits/stdc++.h>
#define int long long
using namespace std;
struct Node{
int use,value ;
bool operator < (Node no1) const{
if(use!=no1.use)return use < no1.use ;
else return value < no1.value ;
}
};
int n , m ;
Node a[100005] ;
int p[100005] ;
int fr(int x){
return x == p[x] ? p[x] : p[x] = fr(p[x]);
}
void merge(int x , int y){
x = fr(x) , y = fr(y) ;
if(a[x].value<a[y].value){
p[y] = x ;
a[x].use = 1 ;
a[y].use = 0 ;
}else{
p[x] = y ;
a[y].use = 1 ;
a[x].use = 0 ;
}
}
signed main(){
//freopen("in","r",stdin);
scanf("%lld%lld",&n,&m);
for(int i = 0 ; i < n ; i++) {
scanf("%lld",&a[i].value);
a[i].use = 1 ;
p[i] = i ;
}
for(int i = 0 ; i < m ; i++) {
int x , y ;
scanf("%lld%lld",&x,&y);
merge(x,y);
}
int ans = 0 ;
int cc = 0 ;
for(int i = 0 ; i < n ; i++) {
if(p[i] == i){
ans = ans + a[i].value;
cc++;
}
}
if(cc==1){
cout << 0 << endl ;return 0 ;
}
if(n<(cc-1)*2){
puts("Impossible");return 0 ;
}
sort(a,a+n);
for(int i = 0 ; i < cc - 2 ; i++) ans = ans + a[i].value ;
cout << ans << endl ;
return 0 ;
}