[HNOI2008]玩具装箱TOY
洛谷题目链接
动态规划\(+\)单调队列\(+\)斜率优化
身为\(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;
}

浙公网安备 33010602011771号