BZOJ 2654: tree【WQS二分+最小生成树】
2654: tree
Time Limit: 30 Sec Memory Limit: 512 MB
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
HINT
原数据出错,现已更新 by liutian,但未重测—2016.6.24
题解
这题其实很好想,就是MST(建议Kruskal),但是我们要保证有need条白色边,那么就可以这样,将白色边权增大,这样肯定会先选黑色边,然后计算答案是减去就可以了。但我们不知道应该加上多少,但是我们能够知道答案是单调的,所以我们可以二分来确定大小。
代码如下
#include<cstdio>
#include<algorithm>
using namespace std;
int n,m,e,ans,alc,fa[50005];
struct xcw{
int x,y,w,c;
bool operator <(const xcw b)const{return (w<b.w)||(w==b.w&&c<b.c);}
}a[100005],E[100005];
int get(int x){return fa[x]==x?x:fa[x]=get(fa[x]);}
bool check(int x){
int num;alc=num=0;
for(int i=0;i<=n;i++) fa[i]=i;
for(int i=1;i<=m;i++){
E[i]=a[i];
if(!E[i].c) E[i].w+=x;
}
sort(E+1,E+1+m);
for(int i=1;i<=m;i++){
int fx=get(E[i].x),fy=get(E[i].y);
if(fx^fy){
fa[fy]=fx,alc+=E[i].w;
if(!E[i].c) num++;
}
}
return num>=e;
}
int main(){
// freopen("prob.in","r",stdin);
// freopen("prob.out","w",stdout);
scanf("%d%d%d",&n,&m,&e);
for(int i=1;i<=m;i++) scanf("%d%d%d%d",&a[i].x,&a[i].y,&a[i].w,&a[i].c);
for(int L=-105,R=105,mid=(R+L)>>1;L<=R;mid=(R+L)>>1)
if(check(mid)) L=mid+1,ans=alc-mid*e;else R=mid-1;
printf("%d\n",ans);
return 0;
}