1. 程式人生 > 其它 >P3195 [HNOI2008]玩具裝箱

P3195 [HNOI2008]玩具裝箱

技術標籤:P3195HNOI2008玩具裝箱

文章目錄

R e s u l t Result Result

...


H y p e r l i n k Hyperlink Hyperlink

https://www.luogu.com.cn/problem/P3195


D e s c r i p t i o n Description Description

一個長度為 n n n的序列 C C C,你需要將其分為若干段,對於一段 [ i , j ] [i,j] [i,j],其代價為 ( j − i + ∑ k = i j C k − L ) 2 (j-i+\sum_{k=i}^jC_k-L)^2 (ji+k=ijCkL)2,其中 L L L是給定的常數,試確定一種劃分方案,使得代價最小

資料範圍: n ≤ 5 × 1 0 4 , L , C i ≤ 1 0 7 n\leq 5\times 10^4,L,C_i\leq 10^7 n5×

104,L,Ci107


S o l u t i o n Solution Solution

d p dp dp
設字首和 s i = ∑ j = 1 i C i s_i=\sum _{j=1}^i C_i si=j=1iCi f i f_i fi表示劃分到 i i i的最優代價
容易得到 f i = m i n 0 < j ≤ i { f j + w ( j , i ) } f_i=min_{0<j\leq i}\{f_j+w(j,i)\} fi=min0<ji{fj+w(j,i)}
其中 w ( i , j ) = ( j − i − 1 + s j − s i − L ) 2 w(i,j)=(j-i-1+s_j-s_i-L)^2

w(i,j)=(ji1+sjsiL)2

然後可以通過四邊形不等式、打表等方法,我們可以證明/發現 f f f具有決策單調性

建立單調棧(偽) s t k stk stk,記錄每個決策點以及其能夠影響的區間,初始所有 f f f的決策點都是0,每次用棧底去更新答案(這tm是單調棧???)

對於一個新的決策 i i i,與棧頂比較,若更優,則棧頂出棧
接著二分一個轉折點 u u u,在 u u u之前都是 t o p top top對應決策更優, u u u之後包括 u u u都是決策 i i i更優,若合法將其入棧

最後檢驗棧底是否合法(即能否對接下來的做出貢獻,即判斷 i i i是否等於棧底可控制的範圍,如果等於,因為下一次 i i i會變大,它就無法做出貢獻,所以要彈出)【這tm不是單調佇列?!!!】

時間複雜度: O ( n log ⁡ 2 n ) O(n\log_2 n) O(nlog2n)
當然還有更優秀的斜率優化 O ( n ) O(n) O(n)做法,具體可以看我之前部落格


C o d e Code Code

#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
#define LL long long
#define N 50010
using namespace std;int top,c[N],n,now;
LL f[N],L,s[N];
inline LL read()
{
	char c;LL d=1,f=0;
	while(c=getchar(),!isdigit(c)) if(c=='-') d=-1;f=(f<<3)+(f<<1)+c-48;
	while(c=getchar(),isdigit(c)) f=(f<<3)+(f<<1)+c-48;
	return d*f;
}
inline LL Cost(int i,int j){LL x=j-i-1+s[j]-s[i]-L;return f[i]+x*x;}
struct node{int l,r,x;}stk[N];
inline int findx(int i)
{
	int L=stk[top].l,R=stk[top].r,mid;
	while(L<=R)
	{
		mid=L+R>>1;
		if(Cost(i,mid)<Cost(stk[top].x,mid)) R=mid-1;else L=mid+1;
	}
	return L;
}
signed main()
{
	n=read();L=read();
	for(register int i=1;i<=n;i++) c[i]=read(),s[i]=s[i-1]+c[i];
	stk[top=1]=(node){1,n,0};now=1;
	for(register int i=1;i<=n;i++)
	{
		f[i]=Cost(stk[now].x,i);
		while(i<stk[top].l&&Cost(i,stk[top].l)<Cost(stk[top].x,stk[top].l)) top--;
		int u=findx(i);stk[top].r=u-1;
		if(u<=n) stk[++top]=(node){u,n,i};
		if(i==stk[now].r) now++;
	}
	printf("%lld",f[n]);
}