• bnuoj25660 Two Famous Companies


    题目链接:https://www.bnuoj.com/v3/problem_show.php?pid=25660

    这个二分真的是烧脑QAQ,想了一晚上才懂了一个大概。

    首先,整体思路是二分,直观上感受一下,就是给第0类边加一个权值,这个权值越大,会让第0类边选的个数变少;权值变小,会上第0类边的个数变多。所以二分这个权值,使得选出来的边的个数是k。

    关键是怎么去做这个二分。对于一个mid(当前的权值二分点),我们去做最小生成树的时候,要给边排序,在给边排序的时候,第一关键字是边权,第二关键字是类别,要让同等权值的第0类排在前面。这样可以保证,做最小生成树的时候,求得的解是所有最小生成树里第0类边的个数最多的。当然,最小生成树中也可能存在比这个条数少的。

    那么假设我们现在做最小生成树,得到了一个选择t条第0类边的解。如果t<k,那这个权值肯定是没戏了,最多能选出来的第0类边都不够k条,因此权值需要变小一点,让边数变多一点;如果t>=k,说明这里面可能有一个包含k条第0类边的解,所以权值(说不定)可以更大一些。

    那么这里要认清一个事实:对于k条第0类边的解来说,随着权值的上升,得到的最小生成树的权值必定是不减的,而且选出来的第0类边的个数必定是不增的。假设原来给第0类边加的权值是X,现在是X',最小生成树的权值是W,权值上升后是W',原来的条数是C,权值上升后是C'。那么有X'>X,W'>=W,C'<=C。如果两个里面都有一个包含k条第0类边的解,那么哪一个更优呢?W'-kX'和W-kX,貌似看不出来啊QAQ。但是结论是,所有k条第0类边构成的解的最小生成树,W-kX都是一样的。为什么呢?其实可以这样考虑,每一个增加权值构成的新的图,形成的最小生成树,都跟原图的一个生成树对应。那么假设原图有若干个恰好包含k条第0类边的权值最小的生成树。那么每一个增加权值后得到的k个第0类边的最小生成树都是跟原来的一一对应的。

    那么也就是说我们只需要找到一个即可。那么这个解该怎么找呢?

    答案是:就是找让t>=k的那个最大的mid一定有一个包含k条第0类边的解。这个可以用反证法。假设这个不包含=k的解。而又有t>=k,所以t>k。那么随着mid减小,t会越来越大,那所有的都没有包含k的解了。但是题目说一定存在一个包含k条第0类边的解,因此矛盾了。

    天啊,终于说完了。直接看代码吧。

    #include<bits/stdc++.h>
    using namespace std;
    
    const int maxn=100005;
    struct Edge
    {
        int u,v,w,x,id;
        Edge(){}
        Edge(int _u,int _v,int _w,int _x):u(_u),v(_v),w(_w),x(_x){}
        bool operator<(Edge&e)
        {
            return w<e.w||w==e.w&&x<e.x;
        }
    };
    Edge edge_ini[maxn];
    Edge edge[maxn];
    int fa[50005];
    const int INF=0x3f3f3f3f;
    
    int findfa(int x)
    {
        if (x==fa[x]) return x;
        return fa[x]=findfa(fa[x]);
    }
    
    int main()
    {
        int n,m,k;
        int cas=0;
        while (~scanf("%d%d%d",&n,&m,&k))
        {
            for (int i=0;i<m;i++)
            {
                scanf("%d%d%d%d",&edge_ini[i].u,&edge_ini[i].v,&edge_ini[i].w,&edge_ini[i].x);
                edge_ini[i].id=i;
            }
            int l=-101,r=101;
            int ans=INF;
            while (l<=r)
            {
                int mid=(l+r)/2;
                for (int i=0;i<m;i++)
                {
                    edge[i].u=edge_ini[i].u;
                    edge[i].v=edge_ini[i].v;
                    edge[i].id=edge_ini[i].id;
                    edge[i].x=edge_ini[i].x;
                    if (edge_ini[i].x==0)
                    {
                        edge[i].w=edge_ini[i].w+mid;
                    }
                    else
                    {
                        edge[i].w=edge_ini[i].w;
                    }
                }
                sort(edge,edge+m);
                int now=0;
                int now0=0;
                int t=0;
                for (int i=0;i<n;i++) fa[i]=i;
                for (int i=0;i<m;i++)
                {
                    if (now==n-1) break;
                    int u=edge[i].u;
                    int v=edge[i].v;
                    int x=edge[i].x;
                    int f1=findfa(u);
                    int f2=findfa(v);
                    if (f1!=f2)
                    {
                        now++;
                        if (x==0) now0++;
                        fa[f1]=f2;
                        t+=edge[i].w;
                    }
                }
                if (now0>=k)
                {
                    l=mid+1;
                    ans=t-k*mid;
                }
                else r=mid-1;
            }
            printf("Case %d: %d
    ",++cas,ans);
        }
        return 0;
    }
  • 相关阅读:
    敏捷软件开发和极限编程介绍
    内存泄漏检测方法
    cocopods 安装
    JavaScript学习笔记-数组(1)
    JavaScript对象应用-字符串和图片对象
    JavaScript基础-面向对象编程<2>
    JavaScript基础-面向对象编程<1>
    JavaScript基础-对象<2>
    JavaScript基础-对象<1>
    高性能朋友圈
  • 原文地址:https://www.cnblogs.com/acmsong/p/8157302.html
Copyright © 2020-2023  润新知