二分答案,先求一个满足相邻两项之差不大于答案时每个位置的最大值b[i]。
设当前位置为i,二分的答案为x,那么需要满足对于j∈[1,n]b[i]=min(a[j]+|i−j|∗x)
对于j<i直接维护最小值,对于j>i用单调队列。
然后枚举0的位置,那么受影响区间的左右端点都单调。
求一个前缀和后可以O(1)计算答案的增加。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define N 1100000
int n,h,r;
ll m,sum[N];
int a[N],pos[N],b[N],q[N];
int cmp(int x,int y){return a[x]<a[y];}
int check(int x)
{
h=1;r=0;
for(int i=1;i<=n;i++)
{
while(r&&a[q[r]]+(ll)q[r]*x>=a[i]+(ll)i*x)
r--;
q[++r]=i;
}
ll v=0,mn=1ll<<60;
for(int i=1;i<=n;i++)
{
b[i]=a[i];
mn=min(mn,a[i]-(ll)i*x);
b[i]=mn+(ll)i*x;
if(q[h]==i)h++;
if(h<=r)b[i]=min((ll)b[i],a[q[h]]+(ll)(q[h]-i)*x);
v+=a[i]-b[i];
}
if(v>m)return 0;
for(int i=1;i<=n;i++)
sum[i]=sum[i-1]+b[i];
for(int i=1,l=1,r=1;i<=n;i++)
{
while(l<i&&b[l]<=(ll)(i-l)*x)
l++;
while(r<n&&b[r+1]>(ll)(r+1-i)*x)
r++;
ll t=v+((sum[i]-sum[l-1])-(ll)(i-l)*(i-l+1)/2*x)+
((sum[r]-sum[i-1])-(ll)(r-i)*(r-i+1)/2*x)-b[i];
if(t<=m)return i;
}
return 0;
}
int main()
{
int l=0,r=0;
scanf("%d%lld",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
r=max(r,a[i]),pos[i]=i;
}
sort(pos+1,pos+1+n,cmp);
while(l<=r)
{
int mid=(l+r)>>1;
if(check(mid))r=mid-1;
else l=mid+1;
}
printf("%d %d\n",check(l),l);
return 0;
}