bzoj 2654 tree

题目:

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值