Description
给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有need条白色边的生成树。
题目保证有解。
Input
第一行V,E,need分别表示点数,边数和需要的白色边数。
接下来E行,每行s,t,c,col表示这边的端点(点从0开始标号),边权,颜色(0白色1黑色)。
Output
一行表示所求生成树的边权和。
V<=50000,E<=100000,所有数据边权为[1,100]中的正整数。
Sample Input
2 2 1
0 1 1 1
0 1 2 0
0 1 1 1
0 1 2 0
Sample Output
2
如果直接kruskal求最小生成树,是无法保证白边数量的,那么我们考虑如果改变白边的数量
可以把白边全部都加上一个权值
也就是我们二分的值,然后跑最小生成树,同时记录白边数量。
但是可能会有无法达到need的情况
例如+2后是need+1,+1后是need-1
于是我们在排序时,边权相同优先把白边往前排,使最小生成树的白边尽量多
反正可以用黑边替换
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 struct Edge 7 { 8 int u,v,dis,c; 9 }a[101001],E[101001]; 10 int ans=2e9,n,m,set[101001],need; 11 bool cmp(Edge a,Edge b) 12 { 13 if (a.dis==b.dis) 14 return a.c<b.c; 15 return a.dis<b.dis; 16 } 17 int find(int x) 18 { 19 if (set[x]!=x) set[x]=find(set[x]); 20 return set[x]; 21 } 22 bool check(int c) 23 {int i,j,cnt,s; 24 memcpy(a,E,sizeof(a)); 25 for (i=1;i<=m;i++) 26 if (a[i].c==0) 27 { 28 a[i].dis+=c; 29 } 30 sort(a+1,a+m+1,cmp); 31 for (i=0;i<=n;i++) 32 set[i]=i; 33 cnt=0;s=0;j=0; 34 for (i=1;i<=m;i++) 35 { 36 int p=find(a[i].u); 37 int q=find(a[i].v); 38 if (p!=q) 39 { 40 set[p]=q; 41 if (a[i].c==0) cnt++; 42 s+=a[i].dis; 43 j++; 44 if (j==n-1) break; 45 } 46 } 47 if (cnt<need) return 0; 48 else 49 { 50 ans=min(ans,s-need*c); 51 return 1; 52 } 53 } 54 int main() 55 {int i; 56 cin>>n>>m>>need; 57 for (i=1;i<=m;i++) 58 { 59 scanf("%d%d%d%d",&E[i].u,&E[i].v,&E[i].dis,&E[i].c); 60 } 61 int l=-100,r=100; 62 while (l<=r) 63 { 64 int mid=(l+r)/2; 65 if (check(mid)) l=mid+1; 66 else r=mid-1; 67 } 68 cout<<ans; 69 }