BZOJ2654:tree(最小生成树,二分)

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

Sample Output

2

Solution 

二分加到白边上的值……加的值越大白边数越少,加的越小白边数越多。

比较函数写错真是gg……

Code

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #define N (200009)
 5 using namespace std;
 6 
 7 struct Edge
 8 {
 9     int u,v,l,c;
10     bool operator < (const Edge &a) const{return l<a.l || l==a.l && c<a.c;}
11 }V[N];
12 int n,m,u,v,w,c,need,e0,e1,refun,wcnt,fa[N];
13 
14 int Find(int x){return x==fa[x]?x:fa[x]=Find(fa[x]);}
15 
16 bool check(int x)
17 {
18     refun=0; wcnt=0;
19     for (int i=1; i<=n; ++i) fa[i]=i;
20     for (int i=1; i<=m; ++i) if (V[i].c==0) V[i].l+=x;
21     sort(V+1,V+m+1);
22     for (int i=1; i<=m; ++i)
23     {
24         int fx=Find(V[i].u), fy=Find(V[i].v);
25         if (fx!=fy) wcnt+=(V[i].c^1), fa[fx]=fy, refun+=V[i].l;
26     }
27     for (int i=1; i<=m; ++i) if (V[i].c==0) V[i].l-=x;
28     return wcnt>=need;
29 }
30 
31 int main()
32 {
33     scanf("%d%d%d",&n,&m,&need);
34     for (int i=1; i<=m; ++i)
35     {
36         scanf("%d%d%d%d",&u,&v,&w,&c); u++; v++;
37         V[i]=(Edge){u,v,w,c};
38     }
39     int l=-209,r=209,ans;
40     while (l<=r)
41     {
42         int mid=(l+r)>>1;
43         if (check(mid)) ans=mid,l=mid+1;
44         else r=mid-1;
45     }
46     check(ans); printf("%d\n",refun-need*ans);
47 }
posted @ 2018-09-16 11:29  Refun  阅读(190)  评论(0编辑  收藏  举报