zoukankan      html  css  js  c++  java
  • [BZOJ 2654]tree(陈立杰)

    [BZOJ 2654]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

    V<=50000,E<=100000,所有数据边权为[1,100]中的正整数。

    题解:
    一道思维题,代码打起来实际上很简单。

    二分+kruskal
    如果直接kruskal求最小生成树,是无法保证白边数量的,那么我们考虑如果改变白边的数量。我们可以把白边全部都加上一个权值,也就是我们二分的值,然后跑最小生成树,同时记录白边数量。当白边数量>=need时,l=mid+1,否则r=mid−1,更新答案就是这棵生成树的权值和减去所有白边的增量。
    证明:
    我们发现,如果我们给白边增加权值,做最小生成树,由于白边权值增大,导致不容易选白边。记f(x)为给白边增加x(x可为负)权值,做最小生成树后,选白边的数量。可以发现,f(x)随x增大而减小,显然可以二分。
    其次,我们发现,由于黑边的权值是不变的,与白边权值不相互影响。同样由于白边之间关系相对不变,必然选出的need条白边一定是符合题意的。

    注意排序的时候如果权值相同要把白色的放在前面。

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    int n,m,s,ans,mmin;
    struct node
    {
        int from,to,dis,color;
    }edge[100001];
    bool cmp(const node a,const node b){if(a.dis!=b.dis)return a.dis<b.dis;else return a.color<b.color;}
    void change(int mid)
    {
        int i;
        for(i=1;i<=m;i++)
        if(!edge[i].color)edge[i].dis+=mid;
    }
    void change_back(int mid)
    {
        int i;
        for(i=1;i<=m;i++)
        if(!edge[i].color)edge[i].dis-=mid;
    }
    int father[50001];
    int find(int x)
    {
        if(father[x]==x)return x;
        else return father[x]=find(father[x]);
    }
    bool judge(int mid)
    {
        int i,c=0;
        mmin=0;
        change(mid);
        sort(edge+1,edge+m+1,cmp);
        for(i=0;i<=n;i++)father[i]=i;
        for(i=1;i<=m;i++)
        {
            int p=find(edge[i].from),q=find(edge[i].to);
            if(p!=q)
            {
                father[p]=q;
                mmin+=edge[i].dis;
                if(edge[i].color==0)c++;
            }
        }
        change_back(mid);
        return c>=s;
    }
    int main()
    {
        int i,j;
        scanf("%d%d%d",&n,&m,&s);
        for(i=1;i<=m;i++)
        {
            scanf("%d%d%d%d",&edge[i].from,&edge[i].to,&edge[i].dis,&edge[i].color);
        }
        int l=-100,r=100,mid;
        while(l<=r)
        {
            mid=(l+r)>>1;
            if(judge(mid))ans=mmin-mid*s,l=mid+1;
            else r=mid-1;
        }
        printf("%d",ans);
        return 0;
    }
  • 相关阅读:
    【MongoDB】windows平台搭建Mongo数据库复制集(相似集群)(一)
    关于jave在oracle驱动下事务提交与回滚问题
    将其它图片格式转为.eps格式
    学习OpenBlas
    ZOJ3640-Help Me Escape
    向死而生——我修的死亡学分
    iOS对象属性详解
    http状态码介绍
    8080端口被占用
    图片特效
  • 原文地址:https://www.cnblogs.com/huangdalaofighting/p/7367616.html
Copyright © 2011-2022 走看看