我们很容易想到一个O(n^2)
的转移方程:dp[i]=min(dp[i],dp[j]+(i-j-1+sum[i]-sum[j]-L)^2)
(1<=j < i) 其中sum[i]是[1-i]的前缀和,但是这时间复杂度明显是不可行的,可以发现转移方程为了寻找最优的 j 将时间复杂度增加了一个维度,若是将这个过程优化到 log(n)或者O(1),就可以解决此问题了。
我们只有一个转移方程,所以我们应该对现有的转移方程进行分析。
dp[i]=min(dp[i],dp[j]+(i-j-1+sum[i]-sum[j]-L)^2)
设f[x]=sum[x]+x , L=L+1;
所以 dp[i]=min(dp[i],dp[j]+(f[i]-f[j]-L)^2)
如果 j,k< i 并且k>j
我们假设 dp[j]+(f[i]-f[j]-L)^2>dp[k]+(f[i]-f[k]-L)^2
就说明选择k优于选择j
又有t>i ,所以f[t]=f[i]+d (d>0)
如果 dp[j]+(f[t]-f[j]-L)^2>dp[k]+(f[t]-f[k]-L)^2
就说明对于 i 选择k优于选择j,相当于对于大于 i 同样有选择k优于选择j
所以我们要根据 dp[j]+(f[i]-f[j]-L)^2>dp[k]+(f[i]-f[k]-L)^2 -> dp[j]+(f[t]-f[j]-L)^2>dp[k]+(f[t]-f[k]-L)^2
即 dp[j]+(f[i]-f[j]-L)^2>dp[k]+(f[i]-f[k]-L)^2 -> dp[j]+(f[i]+d-f[j]-L)^2>dp[k]+(f[i]+d-f[k]-L)^2
dp[j]+(f[i]+d-f[j]-L)^2=dp[j]+(f[i]-f[j]-L)^2+d^2+2*d*(f[i]-f[j]-L)
dp[k]+(f[i]+d-f[k]-L)^2=dp[k]+(f[i]-f[k]-L)^2+d^2+2*d*(f[i]-f[k]-L)
所以只要 f[i]-f[j]-L>f[i]-f[k]-L 即f[k]>f[j] ,因为f[x]=sum[x]+x 是单调递增的,所以得证。
所以只要 dp[j]+(f[i]-f[j]-L)^2>dp[k]+(f[i]-f[k]-L)^2
成立,我们就知道>=i 选择k优于选择j
我们对方程进一步化简,可以得到 (dp[k]-dp[j]+(f[k]+L)^2-(f[j]+L)^2)/(2*(f[k]-f[j])) < f[i]
所以我们现在知道只要满足这个条件即满足选择k优于选择j 。
我们设 getK(j,k)=(dp[k]-dp[j]+(f[k]+L)^2-(f[j]+L)^2)/(2*(f[k]-f[j])) (k>j)
,可以看成k,j两点的斜率。
为了进一步优化,我们要维护这样一个数列,数列范围为[l,r],x属于[l,r],有getK(x,x+1)< getK(x+1,x+2) (x+1,x+2 属于[l,r]),可以看成斜率递增。
只要getK(k,j)< f[i]就可以知道 i 选择k优于选择j,因为数列是斜率递增的,所以我们只要找到小于f[i]的最大斜率即可,并且 i 得到的最大斜率前面的斜率都可以舍弃了。
因为f[x]也是递增的,前面的斜率小于f[i],就肯定小于f[i+1]。
要是还没理解,就看代码吧
#include<cstdio>
using namespace std;typedef long long ll;
const int maxn=60000;ll dp[maxn];
ll sum[maxn],f[maxn];
ll L;
int que[maxn],l,r;double getK(int k,int j){
return 1.0*(dp[k]-dp[j]+(f[k]+L)*(f[k]+L)-(f[j]+L)*(f[j]+L))/(2.0*(f[k]-f[j]));
}int main(){
int n;scanf("%d%lld",&n,&L);L++;for(int i=1;i<=n;i++){
ll tmp;scanf("%lld",&tmp);sum[i]=sum[i-1]+tmp;f[i]=sum[i]+i;}l=1;r=0;que[++r]=0;for(int i=1;i<=n;i++){
while(l<r&&getK(que[l+1],que[l])<f[i]) l++;int t=que[l];dp[i]=dp[t]+(f[i]-f[t]-L)*(f[i]-f[t]-L);while(l<r&&getK(que[r],que[r-1])>getK(i,que[r])) r--;que[++r]=i; }printf("%lld\n",dp[n]);
}