zoukankan      html  css  js  c++  java
  • bzoj2654 tree

    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]中的正整数。

     
    这题还是蛮好玩的
    首先,直接做MST的话白色边的数量是无法估计的。可能比要求的多,也可能更少
    所以考虑怎样调整白色边的数量
    通过这个思路,可以想到如果把所有白色边的权值加上/减去一个Δ,那么不考虑答案正确性,可以保证这时候MST跑出来之后白色边的数量一定会增加/减少
    那么我们就可以直接二分一个值,使得白边的数量符合要求。
    事实上可以证明当我们限定白边的数量一定的时候,MST的答案也是唯一的
    那么我们在白边上加上Δ之后算出来MST的答案会多出need*Δ,直接减掉就好了
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    struct ed{int x,y,z,color;}e[100010];
    struct bian{int x,y,z,mrk;}a[100010];
    inline bool operator<(const bian &a,const bian &b){return a.z<b.z||a.z==b.z&&a.mrk>b.mrk;}
    int n,m,k,ans,tot,sum,tt;
    int fa[100010];
    inline int getfa(int x){return fa[x]==x?x:fa[x]=getfa(fa[x]);}
    inline void jud(int mid)
    {
        tot=sum=0;
        for (int i=1;i<=n;i++)fa[i]=i;
        for (int i=1;i<=m;i++)
        {
            a[i].x=e[i].x;
            a[i].y=e[i].y;
            a[i].z=e[i].z+(e[i].color*mid);
            a[i].mrk=e[i].color;
        }
        sort(a+1,a+m+1);
        for (int i=1;i<=m;i++)
        {
            int fx=getfa(a[i].x),fy=getfa(a[i].y);
            if (fx==fy)continue;
            if (a[i].mrk)tot++;
            fa[fx]=fy;
            sum+=a[i].z;
        }
    }
    int main()
    {
        n=read();m=read();k=read();
        for (int i=1;i<=m;i++)
        {
            e[i].x=read()+1;
            e[i].y=read()+1;
            e[i].z=read();
            e[i].color=read()^1;
            if (e[i].color)tt++;
        }
        int l=-105,r=105;
        while (l<=r)
        {
            int mid=(l+r)>>1;
            jud(mid);
            if (tot>=k){ans=sum-k*mid;l=mid+1;}
            else r=mid-1;
        }
        printf("%d
    ",ans);
        return 0;
    }
    
    ——by zhber,转载请注明来源
  • 相关阅读:
    treesurgeon
    WatiN
    综艺《燃烧吧!天才程序员》:科技类真人秀凭什么吸引人?它是在消费群体吗?
    海外IT老兵谈996:人才不是加班加出来的,期待有企业能站出来破局
    C语言游戏脚本:利用API 函数实现一个简单的超级玛丽外挂!
    C语言基础丨运算符之逻辑运算符(四)
    40岁程序员被90后训斥不996,这世界怎么了?
    C语言丨关键字signed和unsigned 的使用与区别详解
    C语言基础丨运算符之关系运算符(三)
    最硬核的方式找女朋友:用 VS Code 找对象?还是不看脸的那种?!
  • 原文地址:https://www.cnblogs.com/zhber/p/4254745.html
Copyright © 2011-2022 走看看