题目描述
原题来自:2012 年国家集训队互测
给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有 need 条白色边的生成树。题目保证有解。
输入格式
第一行 V,E,need 分别表示点数,边数和需要的白色边数。
接下来 E 行,每行 s,t,c,col 表示这边的端点(点从 0 开始标号),边权,颜色( 0白色,1 黑色)。
输出格式
一行表示所求生成树的边权和。
样例
样例输入
2 2 1
0 1 1 1
0 1 2 0
样例输出
2
数据范围与提示
对于所有数据,V<=5e4,E<=1e5边权为 [ 1,100 ] 中的正整数。
___________________________________________________
想要加入的边数刚好是need条,需要在边上附加权值,控制加入的多少。
所以二分增加的权值,让后最小生成树。
本来很简单,但是刚开始的方法是当条数为need时统计加权以前的边的权值之和,这样会出现所有白边黑白等长而由于白边在前,从而无法取到黑边,白边又超过need的情况,无法解决。所以改为统计加权前的边的和再减去增加权乘以need的积,答案正确。为什么need条数不对还正确呢?因为黑白边等长!!!
___________________________________________________
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=5e4+10; 4 const int maxm=1e5+10; 5 int n,m,need; 6 struct edge 7 { 8 int u,v,w,ww,c; 9 }e[maxm]; 10 bool cmp(edge a,edge b) 11 { 12 return a.ww==b.ww?a.c<b.c:a.ww<b.ww; 13 } 14 int fa[maxm]; 15 int find(int x) 16 { 17 return fa[x]==x?x:fa[x]=find(fa[x]); 18 } 19 int ans; 20 int pd(int x) 21 { 22 for(int i=0;i<m;++i)e[i].ww=e[i].w+(e[i].c==0?x:0); 23 sort(e,e+m,cmp); 24 for(int i=1;i<=n;++i)fa[i]=i; 25 ans=0; 26 int cntb=0,cnt=0; 27 for(int i=0;i<m;++i) 28 { 29 int a=find(e[i].u),b=find(e[i].v); 30 if(a!=b) 31 { 32 cnt++; 33 ans+=e[i].ww; 34 fa[a]=b; 35 if(e[i].c==0)cntb++; 36 if(cnt==n-1)break; 37 } 38 } 39 return cntb; 40 } 41 int da; 42 int main() 43 { 44 scanf("%d%d%d",&n,&m,&need); 45 for(int i=0;i<m;++i) 46 { 47 scanf("%d%d%d%d",&e[i].u,&e[i].v,&e[i].w,&e[i].c); 48 e[i].u++; 49 e[i].v++; 50 } 51 int l=-101,r=101; 52 while(l<=r) 53 { 54 int mid=(l+r)>>1,tf=pd(mid); 55 if(tf>=need) 56 { 57 l=mid+1; 58 da=ans-need*mid; 59 } 60 else 61 { 62 r=mid-1; 63 } 64 } 65 cout<<da; 66 return 0; 67 }