2019ICPC南昌站E.Bob's Problem

题意:

  一张图,n个点,m条边分为黑边和白边,黑边任意选,白边只能选k条,在保持整张图连通的情况下使得所选变的权值和最大

解析:

  因为边权全部是正值,所以可以把黑边全选上,缩点之后对各个连通块和白边进行一次最大生成树,对没有选择的白边再做一次贪心

代码:

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <cstdio>
#include <queue>
#include <cmath>
#include <map>
#include <set>

using namespace std;

typedef long long ll;
const int mod=1e9+7;
const int maxn=5e4+10;
const int maxm=5e2+10;

struct edge{
    ll u,v,dis;
    edge(){}
    edge(ll _u,ll _v,ll _dis):u(_u),v(_v),dis(_dis){}
    bool operator<(const edge s)const {
        return dis>s.dis;
    }
};

ll f[maxn];
ll find_set(ll x){
    return f[x]==x?x:f[x]=find_set(f[x]);
}
ll n,m,k;
edge edge0[maxm];
edge edge1[maxm];
edge edge2[maxm];
ll num0,num1,num2;

int main(){
    ll t;
    scanf("%lld",&t);
    while(t--){
        num0=num1=num2=0;
        scanf("%lld%lld%lld",&n,&m,&k);
        for(int i=0;i<=n;i++)f[i]=i;
        for(int i=0;i<m;i++){
            ll u,v,dis,o;
            scanf("%lld%lld%lld%lld",&u,&v,&dis,&o);
            if(o==0){
                edge0[num0++]=edge(u,v,dis);
            }else{
                edge1[num1++]=edge(u,v,dis);
            }
        }

        ll cnt=n;
        ll nnum1=0;
        ll ans=0;

        for(int i=0;i<num0;i++){
            ll x,y;
            x=find_set(edge0[i].u);
            y=find_set(edge0[i].v);
            if(x!=y){
                f[x]=y;
                cnt--;
            }
            ans+=edge0[i].dis;
            //printf("%lld\n",ans);
        }
        sort(edge1,edge1+num1);
        for(int i=0;i<num1;i++){
            ll x,y;
            x=find_set(edge1[i].u);
            y=find_set(edge1[i].v);
            if(x!=y){
                f[x]=y;
                cnt--;
                nnum1++;
                ans+=edge1[i].dis;
            }else{
                edge2[num2++]=edge1[i];
            }
            //printf("%lld\n",ans);
        }

        sort(edge2,edge2+num2);
        for(int i=0;i<num2;i++){
            if(nnum1>=k)break;
            ans+=edge2[i].dis;
            nnum1++;
            //printf("%lld\n",ans);
        }
        if(nnum1>k||cnt>1){
            printf("-1\n");
        }else {
            printf("%lld\n",ans);

        }
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/wz-archer/p/11846808.html