BZOJ[2654]Tree 二分+Kruskal

7 篇文章 0 订阅
1 篇文章 0 订阅

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2654

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

题目大意:给个图,每个边有权值还有颜色(0或1),让你弄一个生成树,要求包含正好k个颜色为0的边(白边)且尽可能小,求这个最小值

感觉这题好神啊..

正常我们做MST时是没办法考虑颜色的,现在的问题就是如何控制白边的数量
发现如果将每条白边的权值都加上一个 δ 的话,MST中白边的数量就会减小,且 δ 越大白边越少,我们就可以二分这个 δ ,再做MST看看包含多少白边,如果这个值 num 则继续增大 δ
有一处细节:当你选白边数量一定时,无论 δ 等于多少,MST的形态总是不变的,这时继续扩大 δ 也不会对解有影响(想一想,为什么)

代码如下:

#include<algorithm>
#include<ctype.h>
#include<cstdio>
using namespace std;
int n,m,l,r,mid,ans,cur,sum,tmp,num;
int fir[50050],f[50050];
inline int read(){
    char c=getchar();
    int x=0,f=1;
    while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}
    while(isdigit(c)){x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    return x*f;
}
struct Data{
    int x,y,k,b;
}a[100050],nex[100050];
inline bool cmp(Data a,Data b){return a.k<b.k;}
int find(int k){return f[k]==k?f[k]:f[k]=find(f[k]);}
inline void link(int x,int y){
    int fx=find(x),fy=find(y);
    f[fx]=fy;
}
inline bool check(int k){
    sum=0;
    for(int i=1;i<=n;i++) f[i]=i;
    for(int i=1;i<=m;i++){
        nex[i].x=a[i].x;nex[i].y=a[i].y;nex[i].k=a[i].k;nex[i].b=a[i].b;
        if(!nex[i].b) nex[i].k+=k;//给白边加上delta
    }
    sort(nex+1,nex+m+1,cmp);//后面是Kruskal
    tmp=0;
    for(int i=1;i<=m;i++){
        int fx=find(nex[i].x),fy=find(nex[i].y);
        if(fx==fy) continue;
        link(fx,fy);
        sum=sum+nex[i].k;
        if(!nex[i].b) tmp++;//记录一下选多少个白边
    }
return tmp>=num;
}
int main(){
    n=read();m=read();num=read();
    for(int i=1;i<=m;i++){
        a[i].x=read();a[i].y=read();a[i].k=read();a[i].b=read();
        a[i].x++;a[i].y++;
    }
    sort(a+1,a+m+1,cmp);
    l=-10005;r=10005;
    while(l<=r){//二分delta值
        mid=(l+r)>>1;
        if(check(mid)){ans=sum-mid*num;l=mid+1;}//记录答案时要减去delta*num(这部分是你多加的)
        else r=mid-1;
    }
    printf("%d",ans);
return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值