P3195 [HNOI2008]玩具装箱 题解
Link
solve
一道比较模板的斜率优化题目
先写出转移方程
\(F[i]\)表示前\(i\)个已经装箱完毕的最优解,\(S[i]\)表示前\(i\)项\(C[i]+1\)的和,
\[F[i]=F[j]+(S[i]-S[j]+L+1)^2(j≤i)
\]
用\(L+1\)代替\(L\)比较好处理,于是方程变成
\[F[i]=F[j]+(S[i]-S[j]+L)^2(j≤i)
\]
我们要把式子变化成\(kx+b\)的形式,
要注意:移项要遵循的原则是:把含有 \(function(i) \ast function(j)\) 的表达式看作斜率 $k $乘以未知数 \(x\),含有 \(F[i]\) 的项必须要在\(b\)的表达式中,含有\(function(j)\) 的项必须在\(y\)的表达式中。如果未知数\(x\) 的表达式单调递减,最好让等式两边同乘个\(−1\),使其变为单增。
\[F[i]=F[j]+(S[i]-S[j]+L)^2(j≤i)
\]
\[F[i]=F[j]+(S[i])^2+2 \ast S[i]\ast S[j] -2 \ast S[i]\ast L +(S[j]+L)^2
\]
\[(2\ast S[i])\ast S[j]+F[i]-(S[i])^2+2 \ast S[i] \ast L=F[j]+(S[j]+L)^2
\]
这里的\(k=(2\ast S[i])\),\(x=S[j]\) ,\(b=S[j]+F[i]-(S[i])^2+2 \ast S[i] \ast L\),\(y=F[j]+(S[j]+L)^2\)
考虑到我们目标直线的\(k=2 \ast S[i]\)是不断递增的,凸包上的斜率也是不断递增的,就想到用单调队列来维护。
要注意一个细节,初始的时候\(Q\)要给\(0\),不能给\(S[1]\),否则就认为\(1\)号必须单独装了
Code
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn=50005;
inline int read(){
int ret=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
while(ch<='9'&&ch>='0')ret=ret*10+ch-'0',ch=getchar();
return ret*f;
}
LL N,L,hed=1,til,Q[maxn],S[maxn],dp[maxn];
inline LL X(LL j){return S[j];}
inline LL Y(LL j){return dp[j]+(S[j]+L)*(S[j]+L);}
inline long double calc(LL i,LL j){return (long double)(Y(i)-Y(j))/(X(i)-X(j));}
int main(){
freopen("P3195.in","r",stdin);
freopen("P3195.out","w",stdout);
N=read();L=read()+1;
for(int i=1;i<=N;i++)S[i]=S[i-1]+read()+1;
Q[++til]=0;
for(int i=1;i<=N;i++){
while(hed<til&&calc(Q[hed],Q[hed+1])<=2*S[i])++hed;
int j=Q[hed];dp[i]=dp[j]+(S[i]-S[j]-L)*(S[i]-S[j]-L);
while(hed<til&&calc(Q[til-1],Q[til])>=calc(Q[til],i))--til;
Q[++til]=i;
}
printf("%lld\n",dp[N]);
return 0;
}