问题 A: 森林扩张
时间限制: 1 Sec 内存限制: 256 MB题目描述
小L走进了一片森林。这片森林由n个点和m条边组成,每个点都有一个大小ai。小L不小心在森林里迷路了,于是TA决定给森林添上几条边,把整片森林连成一棵树,就能走出去了,在点i与点j间连一条边需要花费ai+aj的代价。此外,小L还发现每个点上最多只能添一条边。小L想知道能不能走出森林,如果能的话,最小代价是多少。
输入
第一行两个整数n和m,表示森林的点数与边数。接下来一行,n个以空格分隔的整数,表示每个点的大小。接下来m行,每行两个正整数,描述森林的一条边。保证给出的图是一片森林。
输出
假如不能把森林变成一棵树,输出−1,否则输出最小代价。
样例输入
5 2
1 2 5 3 4
1 3
2 4
样例输出
10
提示
在点1与点2间加入一条边,点4与点5间加入一条边,总代价是1 + 2 + 3 + 4 = 10。
对于30%的数据,所有点的点权都相同;
对于100%的数据,0 ≤ m < n ≤ 105, 0 ≤ ai ≤ 109,保证给出的图是一片森林(可能是一棵完整的树)。
分析:
每个连通分量内的点找一个最小的 然后再在所有点里找剩下需要的点 一共需要 (连通分量个数-1)*2个点 点不够输出-1
扫描二维码关注公众号,回复:
2449182 查看本文章
AC代码:
#include <bits/stdc++.h>
#define mset(a,x) memset(a,x,sizeof(a))
typedef long long ll;
using namespace std;
ll a[1000005];
int pre[1000005];
struct node{
int Index;
ll min;
int ti;
}temp[1000005];
int find(int x){
int r=x;
while (pre[r]!=r){
r=pre[r];
}
int i=x;
int temp;
while (i!=r){
temp=pre[i];
pre[i]=r;
i=temp;
}
return r;
}
void join(int x,int y){
int fx=find(x);
int fy=find(y);
if(fx!=fy){
pre[fx]=fy;
}
}
void init(){for(int i=0;i<=1000001;i++) pre[i]=i;}
int main (){
int n,m;
while (scanf ("%d%d",&n,&m)!=EOF){
init();
for (int i=1;i<=n;i++) scanf ("%lld",&a[i]);
int u,v;
for (int i=0;i<m;i++){
scanf ("%d%d",&u,&v);
join(u,v);
}
for (int i=0;i<=n;i++) temp[i].min=0x3f3f3f3f;
int cnt=0;
for (int i=1;i<=n;i++)
if (find(i)==i) temp[cnt++].Index=i;
if(cnt==1) {
printf("0\n");continue;
}
for (int i=1;i<=n;i++){
int tt=find(i);
int l=0,r=cnt-1;
while (l<=r){
int mid=(l+r)/2;
if (temp[mid].Index==tt){
if(temp[mid].min>a[i]){
temp[mid].min=a[i];
temp[mid].ti=i;
}
break;
}
else if (temp[mid].Index>tt) r=mid-1;
else l=mid+1;
}
}
ll sum=0;
for (int i=0;i<cnt;i++) sum+=temp[i].min,a[temp[i].ti]=0x3f3f3f3f;
sort(a+1,a+1+n);
int flag=0;
for (int i=1;i<=cnt-2;i++){
if (a[i]!=0x3f3f3f3f) sum+=a[i];
else{
flag=1;break;
}
}
if (flag) printf ("-1\n");
else printf ("%lld\n",sum);
}
return 0;
}