D - Forest

在这里插入图片描述
题意:给出一个由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 ;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值