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;
}
posted @ 2018-04-17 14:51  XSamsara  阅读(281)  评论(0编辑  收藏  举报