P3195 [HNOI2008]玩具裝箱 題解
阿新 • • 發佈:2020-11-27
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$,使其變為單增。
這裡的$ k=(2\ast S[i])\(,\)
考慮到我們目標直線的\(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; }