BZOJ2792 : [Poi2012]Well

二分答案mid,将差距先都调到mid以内。

首先从左往右扫,a[i]=min(a[i],a[i-1]+mid)。

然后从右往左扫,a[i]=min(a[i],a[i+1]+mid)。

枚举要变为0的位置,求出L,R使得:

a[L]>(i-L)mid

a[R]>(R-i)mid

此时只需要把[L,i]和[i,R]修改成一个等差数列即可满足条件且代价最小。

注意到随着i的右移,L递增;随着i的左移,R递减,所以可以$O(n)$完成判定。

 

#include<cstdio>
#define N 1000010
typedef long long ll;
int n,i,j,a[N],b[N],l,r,t,mid,z,k;ll m,s[N],f[N],now;
inline void read(int&a){char c;while(!(((c=getchar())>='0')&&(c<='9')));a=c-'0';while(((c=getchar())>='0')&&(c<='9'))(a*=10)+=c-'0';}
inline int min(int a,int b){return a<b?a:b;}
int check(int mid){
  for(b[1]=a[1],i=2;i<=n;i++)b[i]=min(a[i],b[i-1]+mid);
  for(i=n-1;i;i--)b[i]=min(b[i],b[i+1]+mid);
  for(now=0,i=1;i<=n;i++){
    now+=a[i]-b[i],s[i]=s[i-1]+b[i];
    if(now>m)return 0;
  }
  for(i=j=1;i<=n;i++){
    while(j<i&&b[j]<=(ll)(i-j)*mid)j++;
    f[i]=s[i-1]-s[j-1]-(ll)(1+i-j)*(i-j)*mid/2;
  }
  for(i=j=n;i;i--){
    while(j>i&&b[j]<=(ll)(j-i)*mid)j--;
    f[i]+=s[j]-s[i]-(ll)(1+j-i)*(j-i)*mid/2;
  }
  for(i=1;i<=n;i++)if(f[i]+b[i]+now<=m)return i;
  return 0;
}
int main(){
  read(n),scanf("%lld",&m);
  for(i=1;i<=n;i++)read(a[i]),r=r>a[i]?r:a[i];
  while(l<=r)if(t=check(mid=(l+r)>>1))r=(z=mid)-1,k=t;else l=mid+1;
  return printf("%d %d",k,z),0;
}

  

posted @ 2015-08-14 03:23  Claris  阅读(561)  评论(0编辑  收藏  举报