BZOJ-2654 tree

2654: tree
题目链接
时间限制: 30 Sec 内存限制: 512 MB
提交: 2782 解决: 1138
题目描述
给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有need条白色边的生成树。
题目保证有解。
输入
第一行V,E,need分别表示点数,边数和需要的白色边数。
接下来E行,每行s,t,c,col表示这边的端点(点从0开始标号),边权,颜色(0白色1黑色)。
输出
一行表示所求生成树的边权和。
V<=50000,E<=100000,所有数据边权为[1,100]中的正整数。
样例输入
2 2 1

0 1 1 1

0 1 2 0
样例输出
2
提示
原数据出错,现已更新 by liutian,但未重测—2016.6.24

题解
这道题目的想法很棒。

如果白色边边权会变,那么随着这个值的变大,所选白边条数变少。

我们要做的就是控制白边的数量,使得它正好等于need。

由于变化是单调的,所以,我们想到用二分枚举。

如果发现选的白边数≥need,那么L=mid+1。否则R=mid-1。

但是真正把代码写下了,却发现有一种特别的数据过不去:

当前的mid,使得白边数<need,但是mid+1使得白边数>need。

后来看过网上大神的博客才恍然大悟。

扫描二维码关注公众号,回复: 915213 查看本文章

完全不用担心,因为题目保证有解,所以肯定是由于顺序的缘故。

也就是说肯定有一些和白边长度相等的黑边,我们可以把多余的白边替换掉。

若没有可替换的黑边,那么mid再大一些是完全可以的。

所以白边数≥need时就把当前的总代价赋给ans。

别忘了,有若干条白边的代价改变了,记得扣回来!

代码

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;
const int maxn=5e4+5,maxm=1e5+5;
int n,m,ned,fa[maxn],n1,n2,ans;
struct js{
    int x,y,s;
}blk[maxm],wit[maxm];
int read()
{
    int ret=0,f=1;char ch=getchar();
    while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
    while (ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
    return ret*f;
}
int get(int x){return (fa[x]==x)?x:fa[x]=get(fa[x]);}
bool cmp(js x,js y){return x.s<y.s;}
int check(int tem)
{
    for (int i=1;i<=n;i++) fa[i]=i;
    int L1=1,L2=1,cnt=0,tot=n-1;
    js nxt;int p=1,sum=0;
    for (int i=1;i<=m;i++)
    {
        if (wit[L2].s+tem<=blk[L1].s) nxt=wit[L2++],p=1;
        else nxt=blk[L1++],p=0;
        int x=get(nxt.x),y=get(nxt.y);
        if (x==y) continue;fa[x]=y;
        sum+=nxt.s+tem*p;cnt+=p;
        if (!(--tot)) break;
    }
    if (cnt>=ned) ans=sum-(LL)ned*tem;
    //最重要的就是-ned*tem,扣回多出代价
    return cnt;
}
int main()
{
    n=read(),m=read();ned=read();
    for (int i=1;i<=m;i++)
    {
        int x=read()+1,y=read()+1,s=read(),c=read();
        if (c) blk[++n1]=(js){x,y,s};
          else wit[++n2]=(js){x,y,s};
    }
    sort(blk+1,blk+n1+1,cmp);
    sort(wit+1,wit+n2+1,cmp);//分成两组,可以避免二分check时多次排序
    blk[++n1].s=1e9;wit[++n2].s=1e9;
    int L=-105,R=105;ans=1e9;
    while (L<=R)
    {
        int mid=(R-L>>1)+L;
        int ask=check(mid);
        if (ask>=ned) L=mid+1;
        else R=mid-1;
    }
    printf("%d",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/xu0_zy/article/details/79980585