显然f[i]=min(f[j]+(sum[i]-sum[j]+i-(j+1)-l)^2)
sum[i]表示前缀和
但显然这样的时间复杂度无法得满分,于是我们考虑斜率优化。
为了方便书写,令sum[i]=sum[i]+i,l=l+1
方程变为f[i]=min(f[j]+(sum[i]-sum[j]-l)^2)
当k状态对于当前i比j状态更优时(k>j),f[k]+(sum[i]-sum[k]-l)^2<f[j]+(sum[i]-sum[j]-l)^2
通过数学推理,可以得到f[j]+sum[j]*sum[j]-f[k]-sum[k]*sum[k]/(2*(sum[j]-sum[k])<sum[i]-l
我们令不等式左边为K(j,k)
若K(i,j)>K(j,k)则j状态在之后一定不是最优状态。
证明:假设之后一状态w
1.K(j,k)<sum[w]-l,此时k比j优,故j状态不是最优
2.K(j,k)>=sum[w]-l,由条件可推出K(i,j)>sum[w]-l,此时i比j优。证毕!
然后就可以用一个单调队列维护k(i,j)
ac代码
#include<bits/stdc++.h>
#define maxn 100005
#define ll long long
using namespace std;
ll sum[maxn],n,m,f[maxn];
ll q[maxn];
double K(ll j,ll k)
{
return (double)(f[j]+sum[j]*sum[j]-f[k]-sum[k]*sum[k])/(2.0*(sum[j]-sum[k]));
}
ll read()
{
ll x=0,c;while(!isdigit(c=getchar()));
while(x=x*10+c-'0',isdigit(c=getchar()));
return x;
}
int main()
{
n=read();m=read()+1;
for(int i=1;i<=n;i++)sum[i]=sum[i-1]+read();
for(int i=1;i<=n;i++)sum[i]+=i;
int l=0,r=0;
for(int i=1;i<=n;i++)
{
while(l<r&&K(q[l],q[l+1])<=sum[i]-m)l++;
f[i]=f[q[l]]+(sum[i]-sum[q[l]]-m)*(sum[i]-sum[q[l]]-m);
while(l<r&&K(q[r-1],q[r])>=K(q[r],i))r--;
q[++r]=i;
}
printf("%lld\n",f[n]);
return 0;
}