1. 程式人生 > 實用技巧 >P3195 [HNOI2008]玩具裝箱 題解

P3195 [HNOI2008]玩具裝箱 題解

Link

P3195 [HNOI2008]玩具裝箱

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;
}