BZOJ 2654

2654: tree

Time Limit: 30 Sec  Memory Limit: 512 MB
Submit: 2714  Solved: 1114
[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

 

这道题有点奇怪。。。用控制白边大小的方法使得白边的条数得到控制。

但是并没有找到在重载<运算符时的把白边放在前面的用处,而且不加这一句就会wa。。。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define N 100000+5
using namespace std;
int read()
{
    int x=0,f=1;char s=getchar();
    while(s<'0' || s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0' && s<='9'){x=x*10+s-'0';s=getchar();}
    return x*f;
}
struct edge{int s,t,c,col;}b[N],a[N];
bool operator < (edge x,edge y){return x.c<y.c || (x.c==y.c && x.col<y.col);}
int v,e,need,cnt,tot,ans,l,r,fa[N];
int find(int x)
{
    if(x==fa[x])return x;
    fa[x]=find(fa[x]);
    return fa[x];
}
int main()
{
    v=read();e=read();need=read();
    for(int i=1;i<=e;i++)b[i].s=read()+1,b[i].t=read()+1,b[i].c=read(),b[i].col=read();
    l=-100-5,r=100+5;
    while(l<=r)
    {
        int mid=(l+r)>>1,sum=0;tot=0;
        for(int i=1;i<=v;i++)fa[i]=i;
        for(int i=1;i<=e;i++)
        {
            a[i].s=b[i].s,a[i].t=b[i].t;
            a[i].c=b[i].c+mid*(1-b[i].col);
            a[i].col=b[i].col;
        }
        sort(a+1,a+e+1);    
        for(int i=1;i<=e;i++)
        {
            int p=find(a[i].s),q=find(a[i].t);
            if(p==q)continue;
            fa[p]=q; sum+=a[i].c;
            if(!a[i].col)tot++;
        }
        if(tot>=need){ans=sum-need*mid;l=mid+1;}
        else r=mid-1;
    }
    printf("%d\n",ans);
    return 0;
}

 

 

 

posted @ 2018-03-20 20:32  hyf20010101  阅读(194)  评论(0编辑  收藏  举报