题面描述
思路
理解题意之后,发现
斜率优化真是一个大坑
F i F_i Fi为最近一个箱子装到第 i i i件物品的花费
首先我们来看看状态转移方程
(
s
u
m
sum
sum为前缀和)
F i = min ( F i , F j + ( s u m i − s u m j + ( i − ( j + 1 ) ) − L ) 2 ) F_i=\min(F_i,F_j+(sum_i-sum_j+(i-(j+1))-L)^2) Fi=min(Fi,Fj+(sumi−sumj+(i−(j+1))−L)2)
= min ( F i , F j + ( ( s u m i + i ) − ( s u m j + j ) − ( L + 1 ) ) 2 ) =\min(F_i,F_j+((sum_i+i)-(sum_j+j)-(L+1))^2) =min(Fi,Fj+((sumi+i)−(sumj+j)−(L+1))2)
设 s i = s u m i + i , s j = s u m j + j , L = L + 1 s_i=sum_i+i,s_j=sum_j+j,L=L+1 si=sumi+i,sj=sumj+j,L=L+1,
则 F i = min ( F i , F j + ( s i − s j − L ) 2 ) F_i=\min(F_i,F_j+(s_i-s_j-L)^2) Fi=min(Fi,Fj+(si−sj−L)2)
设有 F j + ( s i − s j − L ) 2 ≥ F k + ( s i − s k − L ) 2 ( j < k < i ) ① F_j+(s_i-s_j-L)^2\ge F_k+(s_i-s_k-L)^2(j<k<i) \ \ ① Fj+(si−sj−L)2≥Fk+(si−sk−L)2(j<k<i) ①
说明在 i i i时, k k k优于 j j j。
那么对于后面状态 t t t呢,是否有 F j + ( s t − s j − L ) 2 ≥ F k + ( s t − s k − L ) 2 F_j+(s_t-s_j-L)^2\ge F_k+(s_t-s_k-L)^2 Fj+(st−sj−L)2≥Fk+(st−sk−L)2
证明:
显而易见的, s t = s i + v a l ( v a l > 0 ) s_t=s_i+val(val>0) st=si+val(val>0),则
F j + ( s t − s j − L ) 2 = F j + ( s i − s j − L + v a l ) 2 ② F k + ( s t − s k − L ) 2 = F k + ( s i − s j − L + v a l ) 2 ③ F_j+(s_t-s_j-L)^2=F_j+(s_i-s_j-L+val)^2\ ②\\ F_k+(s_t-s_k-L)^2=F_k+(s_i-s_j- L+val)^2\ ③ Fj+(st−sj−L)2=Fj+(si−sj−L+val)2 ②Fk+(st−sk−L)2=Fk+(si−sj−L+val)2 ③
由 ② ② ②得:
F j + ( s i − s j − L ) 2 + 2 ∗ v a l ∗ ( s i − s j − L ) + v a l 2 F_j+(s_i-s_j-L)^2+2*val*(s_i-s_j-L)+val^2 Fj+(si−sj−L)2+2∗val∗(si−sj−L)+val2
由 ③ ③ ③得
F k + ( s i − s j − L ) 2 + 2 ∗ v a l ∗ ( s i − s k − L ) + v a l 2 F_k+(s_i-s_j-L)^2+2*val*(s_i-s_k-L)+val^2 Fk+(si−sj−L)2+2∗val∗(si−sk−L)+val2
比较上述二式,并结合 ① ① ①,可知
只要证明 s i − s j − L > s i − s k − L s_i-s_j-L>s_i-s_k-L si−sj−L>si−sk−L即可
− s j > − s k -s_j>-s_k −sj>−sk
由于 s j < s k s_j<s_k sj<sk,则不等式成立。
所以在 i i i时, k k k优于 j j j,那么在后面状态时, k k k永远优于 j j j。
如何淘汰 j j j(队头)
F j + ( s i − s j − L ) 2 ≥ F k + ( s i − s k − L ) 2 F j + ( s i − L ) 2 − 2 ∗ s j ∗ ( s i − L ) + s j 2 ≥ F k + ( s i − L ) 2 − 2 ∗ s k ∗ ( s i − L ) + s k 2 F j − F k + s j 2 − s k 2 ≥ 2 ∗ ( s j − s k ) ∗ ( s i − L ) F_j+(s_i-s_j-L)^2\ge F_k+(s_i-s_k-L)^2 \\ F_j+(s_i-L)^2-2*s_j*(s_i-L)+{s_{j}}^{2}\ge F_k+(s_i-L)^2-2*s_k*(s_i-L)+{s_{k}}^{2}\\ F_j-F_k+{s_j}^2-{s_k}^2\ge 2*(s_j-s_k)*(s_i-L) Fj+(si−sj−L)2≥Fk+(si−sk−L)2Fj+(si−L)2−2∗sj∗(si−L)+sj2≥Fk+(si−L)2−2∗sk∗(si−L)+sk2Fj−Fk+sj2−sk2≥2∗(sj−sk)∗(si−L)
由 s k > s j s_k>s_j sk>sj可知,
F j − F k + s j 2 − s k 2 s j − s k ≤ 2 ∗ ( s i − L ) \frac{F_j-F_k+{s_j}^2-{s_k}^2}{s_j-s_k}\le 2*(s_i-L) sj−skFj−Fk+sj2−sk2≤2∗(si−L)
设 x 1 = F j + s j 2 , x 2 = F k + s k 2 , y 1 = s j , y 2 = s k x_1=F_j+{s_j}^2,x_2=F_k+{s_k}^2,y_1=s_j,y_2=s_k x1=Fj+sj2,x2=Fk+sk2,y1=sj,y2=sk
则 c a l c ( j , k ) = x 1 − x 2 y 1 − y 2 ≤ 2 ∗ ( s i − L ) calc(j,k)=\frac{x_1-x_2}{y_1-y_2}\le2*(s_i-L) calc(j,k)=y1−y2x1−x2≤2∗(si−L)
这句话的意思就是 k k k比 j j j更优,所以 j j j(先进队列)就没有存在的必要,要踢去。
由于 s i s_i si随 i i i增大而增大,那么 c a l c ( q h e a d , q h e a d + 1 ) calc(q_{head},q_{head+1}) calc(qhead,qhead+1)随 h e a d head head增大而增大,才符合 h e a d head head为最优解,因此斜率是不断递增的。
踢队尾
我们首先一定要确定 i i i这个状态,要优于 q t a i l q_{tail} qtail
要维护的队列由于 q t a i l − 1 q_{tail-1} qtail−1一定优于 q t a i l q_{tail} qtail
现有一个标准值 2 ∗ ( s t − L ) ( t a i l < t ) 2*(s_t-L)(tail<t) 2∗(st−L)(tail<t)
c a l c ( q t a i l − 1 , q t a i l ) ≥ 2 ∗ ( s t − L ) calc(q_{tail-1},q_{tail)}\ge 2*(s_t-L) calc(qtail−1,qtail)≥2∗(st−L)
如果 q t a i l q_{tail} qtail要优于 i i i,
则 c a l c ( q t a i l , i ) ≥ c a l c ( q t a i l − 1 , q t a i l ) calc(q_{tail},i)\ge calc(q_{tail-1},q_{tail}) calc(qtail,i)≥calc(qtail−1,qtail)
才能确保 q t a i l q_{tail} qtail优于 i i i
反之,当 c a l c ( q t a i l , i ) ≤ c a l c ( q t a i l − 1 , q t a i l ) calc(q_{tail},i)\le calc(q_{tail-1},q_{tail}) calc(qtail,i)≤calc(qtail−1,qtail)时,需要删去队尾。
貌似就没了哦。
最后几句话
本题维护的是一个下凸壳(我的做法是这样的),所以斜率优化真是一个神奇的东西。
AC code
#pragma GCC optimize("Ofast")
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iostream>
#define gc getchar()
#define ll long long
using namespace std;
const int N=5e4+10;
inline void qr(ll &x)
{
x=0;int f=1;char c=gc;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=gc;}
while(c>='0'&&c<='9'){x=x*10+(c^48);c=gc;}
x*=f;
}
void qw(ll x)
{
if(x<0)x=-x,putchar('-');
if(x/10)qw(x/10);
putchar(x%10+48);
}
ll s[N],f[N];
int q[N],l,r;
double calc(int j,int k)
{
return (f[j]-f[k]+s[j]*s[j]-s[k]*s[k])/(double)(s[j]-s[k]);
}
int n,L;
int main()
{
scanf("%d%d",&n,&L);++L;
for(int i=1;i<=n;i++)qr(s[i]),s[i]+=s[i-1]+1;
l=1;r=1;q[1]=0;
for(int i=1;i<=n;i++)
{
while(l<r&&calc(q[l],q[l+1])<=2.0*(s[i]-L))++l;
f[i]=f[q[l]]+(s[i]-s[q[l]]-L)*(s[i]-s[q[l]]-L);
while(l<r&&calc(q[r],i)<=calc(q[r-1],q[r]))--r;
q[++r]=i;
}
qw(f[n]);puts("");
return 0;
}