P3195 [HNOI2008]玩具裝箱
文章目錄
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 (j−i+∑k=ijCk−L)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
n≤5×
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<j≤i{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
然後可以通過四邊形不等式、打表等方法,我們可以證明/發現 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]);
}