zoukankan      html  css  js  c++  java
  • hdu 6982 / 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)1010 Road Discount(二分+最小生成树)

    https://acm.hdu.edu.cn/showproblem.php?pid=6982

    题意:

    n个城市要修n-1条道路使他们联通,有m条道路可以修,价格有原价和折扣价

    问最多可以选k个折扣价时的最小花费

    对于k∈[0,n-1]依次回答

    做这道题,得先会做“边有黑白两色,求恰好有k条白边的最小生成树”

    可以看这里https://www.cnblogs.com/TheRoadToTheGold/p/15131583.html

    这上面的基础上,题目转化为

    原价就是黑边,折扣价就是白边

    对于k∈[0,n-1],求“边有黑白两色,求恰好有k条白边的最小生成树”

    直接枚举k用所有边求最小生成树会超时

    要2个优化

    优化一:

    因为边权<=1000,提前对所有加边权的方式求解,记录使用的白边条数和总权值

    这样二分的时候直接用即可

    做最小生成树的次数为2000次

    不提前求解的话,做最小生成树的次数为n*log(2000) 大约为10000次

    优化二:

    边数太多了

    结论:在黑白两种边的最小生成树中的边,一定属于只有白边的最小生成树或者只有黑边的最小生成树

    这样可以把边数优化到与n同级

    因为

    假设存在一条 黑白两种边的最小生成树中的边e,既不属于只有白边的最小生成树又不属于只有黑边的最小生成树

    把这条边e去掉,会把树分为两部分

    再去对应e的颜色中的最小生成树上,用连接这两部分的边E替换e

    那么含E的最小生成树权值一定<=含e的最小生成树权值

    #include<bits/stdc++.h>
    
    using namespace std;
    
    #define N 1001
    #define M 400001
    
    int n,m,mm;
    struct node
    {
        int u,v,w,c;
    }f[M],e[N<<1];
    
    int use,sum;
    
    int fa[N];
    
    int fuse[N*2+10],fsum[N*2+10];
    
    bool cmp(node p,node q)
    {
        if(p.w!=q.w) return p.w<q.w;
        return p.c<q.c;
    }
    
    int find(int i) { return fa[i]==i ? i : fa[i]=find(fa[i]); }
    
    void check(int x)
    {
        for(int i=1;i<=m;++i) 
            if(!e[i].c) e[i].w+=x;
        sort(e+1,e+m+1,cmp);
        for(int i=1;i<=n;++i) fa[i]=i;
        int now=0,fu,fv,j=1;
        use=sum=0;
        while(now!=n-1)
        {
            fu=find(e[j].u);
            fv=find(e[j].v);
            if(fu!=fv)
            {
                now++;
                fa[fu]=fv;
                if(!e[j].c) ++use;
                sum+=e[j].w;
            }
            ++j;
        }
        for(int i=1;i<=m;++i) 
            if(!e[i].c) e[i].w-=x;
    }
    
    void pre_edge(int col)
    {
        int now=0,j=1,fu,fv;
        for(int i=1;i<=n;++i) fa[i]=i;
        sort(f+1,f+m+1,cmp);
        while(now!=n-1)
        {
            if(f[j].c==col)
            {
                fu=find(f[j].u);
                fv=find(f[j].v);
                if(fu!=fv)
                {
                    now++;
                    fa[fu]=fv;
                    e[++mm]=f[j];
                }            
            }
            ++j;
        }
    }
    
    int main()
    {
        int T;
        int l,r,mid,ans;
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d%d",&n,&m);
            for(int i=1;i<=m;++i) 
            {
                scanf("%d%d%d",&f[i*2-1].u,&f[i*2-1].v,&f[i*2-1].w);
                f[i*2-1].c=1;
                f[i*2].u=f[i*2-1].u;
                f[i*2].v=f[i*2-1].v;
                scanf("%d",&f[i*2].w);
                f[i*2].c=0;
            }
            m<<=1;
            mm=0;
            pre_edge(1);
            pre_edge(0);
            m=mm; 
            for(int k=-1001;k<=1001;++k) 
            {
                check(k);
                fsum[k+1001]=sum;
                fuse[k+1001]=use;
            }
            for(int k=0;k<n;++k)
            {
                l=-1001;
                r=1001;
                while(l<=r)
                {
                    mid=l+r>>1;
                    if(fuse[mid+1001]>=k) 
                    {
                        ans=fsum[mid+1001]-k*mid;
                        l=mid+1;
                    } 
                    else r=mid-1;
                }
                printf("%d
    ",ans); 
            }
        }
    }
    作者:xxy
    本文版权归作者和博客园共有,转载请用链接,请勿原文转载,Thanks♪(・ω・)ノ。
  • 相关阅读:
    C语言I博客作业08
    博客作业6
    C语言I博客作业05
    C语言I博客作业03
    5.C#2.0之不完整类型(完成)
    4.C#2.0之迭代器(完成)
    3.C#2.0之匿名方法(完成)
    2.C#2.0之泛型(完成)
    1.C#2.0之2.0简介(完成)
    18.C#基础之不安全代码(完成)
  • 原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/15131862.html
Copyright © 2011-2022 走看看