BZOJ 2654 [整体二分][MST]

来源:互联网 发布:网络机顶盒检测 编辑:程序博客网 时间:2024/06/07 02:19

Time Limit: 30 Sec  Memory Limit: 512 MB
Submit: 2019  Solved: 825
[Submit][Status][Discuss]

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

Source


[Submit][Status][Discuss]




无法证明算法的真确性(其实是可以证明的,只是太麻烦了,感性理解一下)

#include<iostream>#include<cstring>#include<cstdio>#include<algorithm>using namespace std;const int N = 50005;const int M = 100005;struct data{    int x,y,w,flag;    inline bool operator < (const data &rhs)const{        return (w==rhs.w)?flag<rhs.flag:w<rhs.w;    }}e[M<<2],a[M<<2];int f[N],n,m,tot,k,l=-110,r=110,sum,ans;int find(int x){return f[x]==x?x:f[x]=find(f[x]);}inline int read(){    static char ch;int flag=1,res;    while((ch=getchar())<'0'||ch>'9')if(ch=='-')flag=-1;res=ch-48;    while((ch=getchar())>='0'&&ch<='9')res=res*10+ch-48;    return res*=flag;}bool judge(int x){    tot=sum=0;    for(register int i=1;i<=n;++i)f[i]=i;    for(register int i=1;i<=m;i++)        a[i].y=e[i].y,a[i].w=e[i].w+e[i].flag*x,a[i].x=e[i].x,a[i].flag=e[i].flag;    sort(a+1,a+1+m);    for(register int i=1;i<=m;i++){        if(find(a[i].x)!=find(a[i].y)){            f[find(a[i].x)]=find(a[i].y);            if(a[i].flag)++tot;            sum+=a[i].w;        }    }    return tot>=k;}int main(){    n=read(),m=read(),k=read();    for(register int i=1;i<=m;++i)        e[i].x=read()+1,e[i].y=read()+1,e[i].w=read(),e[i].flag=read()^1;    while(l<=r){        int mid=(l+r)>>1;        if(judge(mid))ans=sum-mid*tot,l=mid+1;        else r=mid-1;    }    printf("%d\n",ans);    return 0;}
原创粉丝点击