题目大意:
给n个数,每个数可以减去一个非负整数,总修改代价为所有这些非负整数的和,求总代价在m之内且至少有一个数修改为0,max{|Xi - Xi+1|}的最小值。
题解:
考虑二分最小值,对于每个数求出最小代价。
但这样不保证会有数为0。
考虑枚举0的位置,这样一定会在左右分别形成一个等差数列,发现两个等差数列的端点都单调,所以我们双指针扫一遍就可以了。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1000005;
typedef long long ll;
int read(){
int x=0,f=1; char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') f=-1; ch=getchar();}
while (ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
int n,b[N],a[N];
ll s[N],f[N],m;
int pd(int mid){
b[1]=a[1];
ll now=0;
for (int i=2;i<=n;i++) b[i]=min(a[i],b[i-1]+mid);
for (int i=n-1;i>=1;i--) b[i]=min(b[i],b[i+1]+mid);
for (int i=1;i<=n;i++) now+=a[i]-b[i],s[i]=s[i-1]+b[i];
if (now>m) return 0;
int l=1;
for (int i=1;i<=n;i++){
while (l<i&&b[l]<=(ll)(i-l)*mid) l++;
f[i]=s[i-1]-s[l-1]-(ll)(i-l+1)*(i-l)*mid/2;
}
int r=n;
for (int i=n;i>=1;i--){
while (r>i&&b[r]<=(ll)(r-i)*mid) r--;
f[i]+=s[r]-s[i]-(ll)(r-i+1)*(r-i)*mid/2;
}
for (int i=1;i<=n;i++)
if (f[i]+b[i]+now<=m) return i;
return 0;
}
int main(){
n=read(); scanf("%lld",&m);
int L=0,R=0,ans1=0,ans2=0;
for (int i=1;i<=n;i++) a[i]=read(),R=max(R,a[i]);
while (L<=R){
int t,mid=(L+R)>>1;
t=pd(mid);
if (t){R=mid-1; ans1=t,ans2=mid;} else L=mid+1;
}
printf("%d %d\n",ans1,ans2);
return 0;
}