【bzoj2654】tree MST+二分法

Description

  给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有need条白色边的生成树。
  题目保证有解。

Input

  第一行V,E,need分别表示点数,边数和需要的白色边数。
  接下来E行
  每行s,t,c,col表示这边的端点(点从0开始标号),边权,颜色(0白色1黑色)。

Output

  一行表示所求生成树的边权和。

Sample Input

2 2 1

0 1 1 1

0 1 2 0

Sample Output

2

HINT

数据规模和约定

  0:V<=10

  1,2,3:V<=15

  0,..,19:V<=50000,E<=100000

  所有数据边权为[1,100]中的正整数。

Source


CLJ的题 Orz

很奇妙的二分…若给白边加上一个值,则做MST的话选取白边数量不增。

那样就二分这个值,看看选取白边数量,做MST就行了…

还有就是cmp的写法,同边权优先取白边。这样若白边边权+mid则比need小,+mid+1则比need大,则说明同边权既有白边又有黑边,白边可以换成黑边。这样得到的白边数量>=mid则可以更新答案。

#include<cstdio>
#include<iostream> 
#include<cstring>
#include<algorithm>
using namespace std;

const int SZ = 1000010;
const int INF = 1000000010;

int n,m,nd;

struct edge{
    int f,t,d,c;
}l[SZ];

bool cmp(edge a,edge b)
{
    return a.d == b.d ? a.c < b.c : a.d < b.d;
}

int fa[SZ];

int find(int x)
{
    return x == fa[x] ? x : fa[x] = find(fa[x]);
}

int mst = 0;
int MST(int mid)
{
    for(int i = 1;i <= n;i ++) fa[i] = i;
    for(int i = 1;i <= m;i ++)
        if(!l[i].c) l[i].d += mid;

    mst = 0;
    int ans = 0;
    sort(l + 1,l + 1 + m,cmp);
    for(int i = 1;i <= m;i ++)
    {
        int x = find(l[i].f);
        int y = find(l[i].t);
        if(x != y)
        {
            if(!l[i].c) ans ++;
            mst += l[i].d;
            fa[x] = y;
        }
    }

    for(int i = 1;i <= m;i ++)
        if(!l[i].c) l[i].d -= mid;  
    return ans;
}


int div()
{
    int s = -110,t = 110,ans;
    while(t - s > 1)
    {
        int mid = (s + t) >> 1;
        int tmp = MST(mid);
    //  cout<<mst<<endl;
        if(tmp >= nd) s = mid,ans = mst - mid * nd;
        else t = mid; 
    }
//  printf("%d %d\n",s,t);
    return ans;
}

int main()
{
    scanf("%d%d%d",&n,&m,&nd);
    for(int i = 1;i <= m;i ++)
    {
        scanf("%d%d%d%d",&l[i].f,&l[i].t,&l[i].d,&l[i].c);
        l[i].f ++;  l[i].t ++;
    }
    printf("%d",div());
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值