题目大意:给n个数,每个数可以减去一个非负整数,总修改代价为所有这些非负整数的和,求总代价在m之内且至少有一个数修改为0,max{|Xi - Xi+1|}的最小值
先二分答案,然后对于每次的值求一个最小代价
首先可以正反扫两遍求出每个点最大合法答案,在没有必须有0这个条件时,这个方案一定是最小的代价
然后枚举0的位置,这样左右一定都是一个等差数列,这样对于每个数来说都对应一个区间,区间两个端点分别单调,提前扫出来就好了
时间复杂度O(NlogINF)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define N 1000010
using namespace std;
long long n,m;
long long a[N],b[N];
long long L[N],R[N],pre[N],ans;
bool check(long long x)
{
long long i,j;
for(i=1;i<=n;i++)
b[i]=a[i];
long long tot=0;
for(i=2;i<=n;i++)
{
if(b[i]-b[i-1]>x)
{
tot+=b[i]-b[i-1]-x;
b[i]=b[i-1]+x;
}
}
for(i=n-1;i>=1;i--)
{
if(b[i]-b[i+1]>x)
{
tot+=b[i]-b[i+1]-x;
b[i]=b[i+1]+x;
}
}
j=1;
for(i=1;i<=n;i++)
{
while(b[j]<=(i-j)*x&&i>j) j++;
L[i]=j;
}
j=n;
for(i=n;i>=1;i--)
{
while(b[j]<=(j-i)*x&&i<j) j--;
R[i]=j;
}
for(i=1;i<=n;i++)
pre[i]=pre[i-1]+b[i];
for(i=1;i<=n;i++)
{
if(tot+pre[R[i]]-pre[L[i]-1]-x*((R[i]-i)*(R[i]-i+1)+(i-L[i])*(i-L[i]+1))/2<=m)
{
ans=i;
return true;
}
}
return false;
}
int main()
{
long long i,j;
scanf("%lld%lld",&n,&m);
for(i=1;i<=n;i++)
scanf("%lld",&a[i]);
long long l=0,r=1e9,mid;
while(l<r)
{
mid=(l+r)>>1;
if(!check(mid)) l=mid+1;
else r=mid;
}
printf("%lld %lld",ans,l);
}