动态规划$+$单调队列$+$斜率优化
身为$dp$蒟蒻的我,准备学斜率优化和单调队列,自然不能放过这个经典题目了,我们对于这题很容易想出一个暴力式子:
($sum[i]$为前缀和)
$f[i]=min(f[i],f[j]+(sum[i]-sum[j]+i-j-L-1)^2)$
我们不考虑最小值,把$sum[i]+i$用$a[i]$代替,$sum[j]+j+L+1$用$b[j]$代替,那么式子整理一下如下:
$f[i]=f[j]+(a[i]+b[j])^2$
去括号:
$f[i]=f[j]+a[i]^2+2a[i]b[j]+b[j]^2$
移项:
$f[j]+b[j]^2=2a[i]b[j]+f[i]+a[i]^2$
设$y=f[j]+b[j]^2$,$x=b[j]$,$b=f[i]+a[i]^2$,这就很像一次函数了,那么斜率自然就是$k=2a[i]$了
为什么这么设呢,因为我们发现$y,x,k$在知道$i,j$时可以快速求出
$f[i]$的含义转化为:当上述直线过点$P(b[j],f[j]+b[j]^2)$时,直线在$y$轴的截距加上$a[i]^2$(一个定值)
而题目即为找这个截距的最小值
而且我们仔细一看发现会有单调性,于是用单调队列维护最小值。
代码如下:
// luogu-judger-enable-o2 #include<iostream> #include<cstdio> #define N 50007 #define int long long using namespace std; int n,L; int sum[N],que[N],f[N]; int A(int i) { return sum[i]+i; } int B(int i) { return sum[i]+i+1+L; } int X(int i) { return B(i); } int Y(int i) { return f[i]+B(i)*B(i); } int Get_k(int i,int j) { return (Y(i)-Y(j))/(X(i)-X(j)); } signed main() { scanf("%lld%lld",&n,&L); for(int i=1;i<=n;++i) { scanf("%lld",&sum[i]); sum[i]+=sum[i-1]; } int head=1,tail=1; for(int i=1;i<=n;++i) { while(head<tail&&Get_k(que[head],que[head+1])<2*A(i)) ++head; f[i]=f[que[head]]+(A(i)-B(que[head]))*(A(i)-B(que[head])); while(head<tail&&Get_k(que[tail],que[tail-1])>Get_k(i,que[tail-1])) --tail; que[++tail]=i; } printf("%lld",f[n]); return 0; }