码迷,mamicode.com
首页 > 其他好文 > 详细

[Ioi2011] ricehub

时间:2015-06-03 11:35:54      阅读:152      评论:0      收藏:0      [点我收藏+]

标签:

题目: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\n",r);
    return 0;
}
Code

 

[Ioi2011] ricehub

标签:

原文地址:http://www.cnblogs.com/lawyer/p/4548443.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!