动态规划$+$单调队列$+$斜率优化
身为$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;
}