• [Ioi2011] ricehub


    题目:http://www.lydsy.com/JudgeOnline/problem.php?id=2600

    这道题按说是当年较为水的一道题,但是我没有独立想出。

    相当于是找出一个最长的序列使得所有的数字与中位数差值的绝对值之和(就是运费)最小。

    基本的思路是二分答案,因为如果存在长度为x的一个序列的话,一定也存在长度为x-1的一个序列。

    关键就是O(n)检验二分出的答案。

    可以发现通过分类讨论是可以快速统计所有的长度为L的运费的。

    假设二分出的长度为len。

    初始有一个区间 [1,len] 然后每一次只要从 [l,r] 转移到 [l+1,r+1] 即可。

    很好转移,相当于先删除掉 l 这个位置上的数字与中位数的差值,而后加上 r 这个位置的数与中位数的差值。

    中位数的变化是1或者0,加上(mid-l+1)减去(r-mid)即可。

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    #define LL long long
    #define N 400010
    
    using namespace std;
    
    int n;
    LL L,B,x[N];
    
    inline LL Abs(LL x){
        if(x<0) return -x;
        return x;
    }
    
    inline bool check(int len){
        LL ans=0;
        for(int i=1;i<=len;i++)
            ans+=Abs(x[(len+1+1)>>1]-x[i]);
        if(ans<=B) return 1;
        int mid=(len+1+1)>>1;
        for(int l=2;l<=n-len+1;l++){
            int r=l+len-1;
            ans-=x[mid]-x[l-1];
            mid=(l+r+1)>>1;
            ans+=(mid-l)*(x[mid]-x[mid-1]);
            ans-=(r-mid)*(x[mid]-x[mid-1]);
            ans+=x[r]-x[mid];
            if(ans<=B) return 1;
        }
        return 0;
    }
    
    int main(){
        scanf("%d%lld%lld",&n,&L,&B);
        for(int i=1;i<=n;i++) scanf("%lld",&x[i]);
        int l=1,r=n;
        while(r-l>10){
            int mid=(l+r)>>1;
            if(check(mid)) l=mid;
            else r=mid;
        }
        for(;r>=l;r--) if(check(r)) break;
        printf("%d
    ",r);
        return 0;
    }
    Code
  • 相关阅读:
    页面跳转刷新
    表格表头绘制对角线(不固定表格宽高)
    发送邮件的工具类
    重写equals()和hashCode()
    设计模式--原型模式[转载]
    设计模式--外观模式
    设计模式--代理模式
    js处理json js递归
    MySQL锁详解
    开发一个微信小程序实例教程
  • 原文地址:https://www.cnblogs.com/lawyer/p/4548443.html
Copyright © 2020-2023  润新知