显然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; }