• BZOJ 2654: tree


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

    Source


    HOME Back

    题解:

      这个题目,我们可以先二分一下给白边所加上的权值,因为我们只需要need条边,所以如果大于need,就需要使mid变大,所以l=mid+1,小于need就相反,但是对答案进行更新的时是在加白边总数大于(tmp)>=mid的时候,因为等于肯定可以,然后考虑如果加上mid   tmp>need但加上mid+1, tmp<need的情况,因为我们是在权值相等的情况下优先加入白边,所以可以将一些白边换成黑边.

    代码:

      

    /**************************************************************
        Problem: 2654
        User: 1796681012
        Language: C++
        Result: Accepted
        Time:3052 ms
        Memory:3248 kb
    ****************************************************************/
     
    #include <cstdio>
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <cmath>
    #include <iostream>
    #define MAXN 100100
    using namespace std;
    int fa[MAXN];
    struct edge{
        int from,to,quan,id;
        void read(){
            scanf("%d%d%d%d",&from,&to,&quan,&id);from++,to++;
        }
    }a[MAXN];
    int ans,tmp=0,n,m,need,tot;
     
    bool cmp(edge x,edge y){
        if(x.quan==y.quan) return x.id<y.id;
        return x.quan<y.quan;
    }
     
    int find(int now){
        if(fa[now]!=now) fa[now]=find(fa[now]);
        return fa[now];
    }
     
    void work(){
        tmp=0,tot=0;int hh=0;
        sort(a+1,a+m+1,cmp);
        for(int i=1;i<=m;i++){
            int x=a[i].from,y=a[i].to;int fax=find(x),fay=find(y);
            if(fax!=fay){
                fa[fax]=fay;
                tot+=a[i].quan;hh++;
                if(a[i].id==0) tmp++;
            }
            if(hh==n-1) break;
        }
    }
     
    int main()
    {
        scanf("%d%d%d",&n,&m,&need);
        for(int i=1;i<=m;i++) a[i].read();
        int l=-105,r=105,mid;
        while(l<=r){
            mid=(l+r)/2;
            for(int i=1;i<=n;i++) fa[i]=i;
            for(int i=1;i<=m;i++) if(a[i].id==0) a[i].quan+=mid;
            work();
            if(tmp>=need) ans=tot-need*mid,l=mid+1;
            else r=mid-1;
            for(int i=1;i<=m;i++) if(a[i].id==0) a[i].quan-=mid;
        }
        printf("%d",ans);
        return 0;
    }
  • 相关阅读:
    面向对象之继承
    面向对象之封装
    进程相关(一)
    面向对象之反射,元类
    实现效果从中间变大
    如何扒一个网站
    java例程练习(引用类型数据的排序和查找)[外篇]
    java例程练习(Iterator)
    java例程练习(增强的for循环)
    java例程练习(Map接口及自动打包、解包)
  • 原文地址:https://www.cnblogs.com/renjianshige/p/9526833.html
Copyright © 2020-2023  润新知