[HNOI2008]玩具装箱TOY

题面描述

传送门

思路

理解题意之后,发现

斜率优化真是一个大坑

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+(sumisumj+(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+(sisjL)2)

设有 F j + ( s i − s j − L ) 2 ≥ F k + ( s i − s k − L ) 2 ( j &lt; k &lt; i )    ① F_j+(s_i-s_j-L)^2\ge F_k+(s_i-s_k-L)^2(j&lt;k&lt;i) \ \ ① Fj+(sisjL)2Fk+(siskL)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+(stsjL)2Fk+(stskL)2

证明:

显而易见的, s t = s i + v a l ( v a l &gt; 0 ) s_t=s_i+val(val&gt;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+(stsjL)2=Fj+(sisjL+val)2 Fk+(stskL)2=Fk+(sisjL+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+(sisjL)2+2val(sisjL)+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+(sisjL)2+2val(siskL)+val2

比较上述二式,并结合 ① ① ,可知

只要证明 s i − s j − L &gt; s i − s k − L s_i-s_j-L&gt;s_i-s_k-L sisjL>siskL即可

− s j &gt; − s k -s_j&gt;-s_k sj>sk

由于 s j &lt; s k s_j&lt;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+(sisjL)2Fk+(siskL)2Fj+(siL)22sj(siL)+sj2Fk+(siL)22sk(siL)+sk2FjFk+sj2sk22(sjsk)(siL)

s k &gt; s j s_k&gt;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) sjskFjFk+sj2sk22(siL)

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)=y1y2x1x22(siL)

这句话的意思就是 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} qtail1一定优于 q t a i l q_{tail} qtail

现有一个标准值 2 ∗ ( s t − L ) ( t a i l &lt; t ) 2*(s_t-L)(tail&lt;t) 2(stL)(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(qtail1,qtail)2(stL)

如果 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(qtail1,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(qtail1,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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值