bzoj 2654 tree

来源:互联网 发布:matlab 定义二维数组 编辑:程序博客网 时间:2024/06/15 14:02

题目:

http://www.lydsy.com/JudgeOnline/problem.php?id=2654

2654: 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

题解:
奇怪的二分。。。
开始的想法是在mst过程中,跳过部分数量的黑边,二分这个数量,check图是否仍然联通,显然,有时跳过前面的黑边不如跳过后边的黑边优;
后来又想先建出有need条最短白边的连通图,再枚举删除哪一条黑边,被时限卡死,枚举是On,判断图是否联通On,且当前白边对图的连通性无贡献时,是否加入也是一个问题。成功hack两波~
正解是:二分一个值,使白边的权值加上这个值,做mst。
显然,不严格意义下:
此值增大,白边的数量减少;
此值减小,白边的数量减少。

细节
如果白边黑边权值相同,优先考虑白边(在cmp中体现);
因为看的题解,所以本来是没有考虑这个问题的;
会出现答案是mid,边数大于need
答案是mid+1边数小于need;
原因在于克鲁斯卡尔的加边顺序,此时一定有权值和对连通性贡献都相同的黑白边。
然而我的cpp可以去掉对于这个细节的处理:
可能是因为最后又做了一遍mst,具体原因等我去问问DQS学长;

在清澄上交,,wa,,被恶心了,等我明天做

代码:

#include<iostream>#include<cstdio>#include<algorithm>using namespace std;const int N=50000+500,M=100000+500;int num,ans,cnt,n,m,ned;struct edge{    int s,t,v,c,val;}e[M];int fa[N];bool cmp(edge a,edge b){    return a.val<b.val;    if(a.val==b.val) return a.c<b.c;}int find(int x){    if(x==fa[x]) return x;    return fa[x]=find(fa[x]);}void init(int x){    num=0;    cnt=0;    ans=0;    for(int i=1;i<=m;i++) e[i].val=((e[i].c+1)%2)*x+e[i].v;    sort(e+1,e+m+1,cmp);    for(int i=0;i<=n;i++) fa[i]=i; }bool check(int x){    init(x);    for(int i=1;i<=m;i++){        int x=find(e[i].s),y=find(e[i].t);        if(x==y) continue;        if(e[i].c==0) cnt++;        num++;        fa[x]=y;        ans+=e[i].v;        if(num==n-1) break;    }    if(cnt>=ned) return false;    return true;}int main(){    scanf("%d%d%d",&n,&m,&ned);    for(int i=1;i<=m;i++){        scanf("%d%d%d%d",&e[i].s,&e[i].t,&e[i].v,&e[i].c);    }    int l=-1000,r=1000;    while(r-l>1){        int mid=r+l>>1;        if(check(mid)) r=mid;        else l=mid;    }    check(l);    printf("%d",ans);    return 0;}
原创粉丝点击