題意:
一張圖,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; }