BZOJ2600_ricehub_KEY

题目传送门

这道题一开始我还以为是贪心,sort一遍直接取中点然后求最优值。

但写了之后才发现错误,设置的谷仓只要是一段区间的中点即可。这段区间的两端一定是两片谷田。

所以枚举区间的左端点,二分右端点,但问题是如何O(1)判断?

设sumi表示1~i点的和。

L~R区间所需要的费用分成两段来求。

一段为L~mid-1,一段为mid+1~R。

L~mid-1=a[mid]*(mid-l)-(sum[k-1]-sum[l-1]);

mid+1~R=sum[r]-sum[k]-a[k]*(r-k);

两者相加即为这段区间的费用总和。

code:

#include <cstdio>
#include <algorithm>
using namespace std;

int read()
{
    char c;while(c=getchar(),c<'0'||c>'9');int x=c-'0';
    while(c=getchar(),c>='0'&&c<='9')x=x*10+c-'0';
    return x;
}

int abs(int x){return x>0?x:-x;}

const int MAXN=100005;

int R,L,a[MAXN],i,ans;
long long B,sum[MAXN];

int find(int l,int r)
{
    long long w=0,k=l+r+1>>1;
    w=a[k]*(k-l)-(sum[k-1]-sum[l-1])+sum[r]-sum[k]-a[k]*(r-k);
    return w<=B;
}

int check(int x)
{
    int l=x,r=R,mid,tot;
        while(l<=r){
            mid=l+r>>1;
            if(find(x,mid))l=mid+1,tot=mid;
            else r=mid-1;
        }
    return tot-x+1;
}

int main()
{
//    freopen("x.txt","r",stdin);
    R=read(),L=read();scanf("%lld",&B);
        for(i=1;i<=R;i++)a[i]=read(),sum[i]=sum[i-1]+a[i];
        for(i=1;i<=R;i++){
            ans=max(ans,check(i));
        }
    printf("%d",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/Cptraser/p/8320771.html