斜率优化DP
状态及转移很好想:
f[i]
表示以i为结尾的玩具所需费用的最小值。
转移:
f[i]=min(f[j]+(∑ik=jc[k]+i−j−1−L)2)
然而 n≤50000 ,于是可以考虑进行斜率优化
假设对
i
而言
f[x]+(∑ik=xc[k]+i−x−1−L)2<f[y]+(∑ik=y+i−y−1−L)2
记一个
c
的前缀和
记
X[i]=C[i]−i−1−L
,
Y[i]=C[i]+i
,则
f[x]+(X[i]−Y[x])2<f[y]+(X[i]−Y[y])2
左边
=f[x]+X[i]2−2X[i]Y[x]+Y[x]2
右边
=f[y]+X[i]2−2X[i]Y[y]+Y[y]2
联立后化简,得
f[x]+Y[x]2−f[y]−Y[y]22(Y[x]−Y[y])>X[i]
然后用单调队列维护一下左边这个东西就行啦
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 50000
using namespace std;
typedef long long LL;
LL f[MAXN+5],c[MAXN+5],X[MAXN+5],Y[MAXN+5];
int n,l,que[MAXN+5];
#define sqr(x) ((x)*(x))
LL K(int x,int y){
return (f[x]+sqr(Y[x])-f[y]-sqr(Y[y]))/(2*(Y[x]-Y[y]));//左边的式子
}
int main(){
scanf("%d%d",&n,&l);
for (int i=1;i<=n;i++){
scanf("%lld",&c[i]),c[i]+=c[i-1];
X[i]=c[i]+i-1-l,Y[i]=c[i]+i;
f[i]=1e18;
}
int r=0,w=0;
for (int i=1;i<=n;i++){//单调队列维护
while (r<w&&K(que[r],que[r+1])<X[i]) r++;
f[i]=f[que[r]]+sqr(X[i]-Y[que[r]]);
while (r<w&&K(que[w-1],que[w])>=K(que[w],i)) w--;
que[++w]=i;
}
return printf("%lld\n",f[n]),0;
}