HNOI 2008 玩具装箱TOY

题目传送门

题目大意: n n n 个物品,你可以对一段连续的物品进行压缩,假如压缩区间 [ l , r ] [l,r] [l,r],那么费用为: ( r − l + ∑ i = l r a [ i ] − L ) 2 (r-l+\sum_{i=l}^r a[i]-L)^2 (rl+i=lra[i]L)2,问将这 n n n 个物品压缩的最小费用。

题解

f [ i ] f[i] f[i] 表示将前 i i i 个物品压缩的最小费用, c [ i ] = ∑ j = 1 i a [ j ] c[i]=\sum_{j=1}^i a[j] c[i]=j=1ia[j],那么有:
f [ i ] = min ⁡ j = 0 i − 1 { f [ j ] + ( i − j − 1 + c [ i ] − c [ j ] − L ) 2 } f[i]=\min_{j=0}^{i-1} \{f[j]+(i-j-1+c[i]-c[j]-L)^2\} f[i]=j=0mini1{f[j]+(ij1+c[i]c[j]L)2}

显然要用斜率优化去搞了,那么接下来就是推柿子了(这种柿子建议自己手推一下,虽然很繁琐,但是获得的成就感以及收获还是很大的):

假如用 j j j 来更新 i i i 比用 k k k 来更新 i i i 更优,那么需要满足:
f [ j ] + ( i − j − 1 + c [ i ] − c [ j ] − L ) 2 < f [ k ] + ( i − k − 1 + c [ i ] − c [ k ] − L ) 2 f[j]+(i-j-1+c[i]-c[j]-L)^2<f[k]+(i-k-1+c[i]-c[k]-L)^2 f[j]+(ij1+c[i]c[j]L)2<f[k]+(ik1+c[i]c[k]L)2

g [ i ] = i + c [ i ] g[i]=i+c[i] g[i]=i+c[i],代入得:
f [ j ] + ( g [ i ] − g [ j ] − 1 − L ) 2 < f [ k ] + ( g [ i ] − g [ k ] − 1 − L ) 2 f[j]+(g[i]-g[j]-1-L)^2<f[k]+(g[i]-g[k]-1-L)^2 f[j]+(g[i]g[j]1L)2<f[k]+(g[i]g[k]1L)2

为了保护各位的眼睛,建议跳过这一段,拆开得:
f [ j ] + g [ i ] 2 − g [ i ] g [ j ] − g [ i ] − g [ i ] L − g [ i ] g [ j ] + g [ j ] 2 + g [ j ] + g [ j ] L − g [ i ] + g [ j ] + 1 + L − g [ i ] L + g [ j ] L + L + L 2 < f [ k ] + g [ i ] 2 − g [ i ] g [ k ] − g [ i ] − g [ i ] L − g [ i ] g [ k ] + g [ k ] 2 + g [ k ] + g [ k ] L − g [ i ] + g [ k ] + 1 + L − g [ i ] L + g [ k ] L + L + L 2 f[j]+g[i]^2-g[i]g[j]-g[i]-g[i]L-g[i]g[j]+g[j]^2+g[j]+g[j]L-\\ g[i]+g[j]+1+L-g[i]L+g[j]L+L+L^2<\\ f[k]+g[i]^2-g[i]g[k]-g[i]-g[i]L-g[i]g[k]+g[k]^2+g[k]+g[k]L-\\ g[i]+g[k]+1+L-g[i]L+g[k]L+L+L^2 f[j]+g[i]2g[i]g[j]g[i]g[i]Lg[i]g[j]+g[j]2+g[j]+g[j]Lg[i]+g[j]+1+Lg[i]L+g[j]L+L+L2<f[k]+g[i]2g[i]g[k]g[i]g[i]Lg[i]g[k]+g[k]2+g[k]+g[k]Lg[i]+g[k]+1+Lg[i]L+g[k]L+L+L2

f [ j ] − g [ i ] g [ j ] − g [ i ] g [ j ] + g [ j ] 2 + g [ j ] + g [ j ] L + g [ j ] + g [ j ] L < f [ k ] − g [ i ] g [ k ] − g [ i ] g [ k ] + g [ k ] 2 + g [ k ] + g [ k ] L + g [ k ] + g [ k ] L f[j]-g[i]g[j]-g[i]g[j]+g[j]^2+g[j]+g[j]L+g[j]+g[j]L<\\ f[k]-g[i]g[k]-g[i]g[k]+g[k]^2+g[k]+g[k]L+g[k]+g[k]L f[j]g[i]g[j]g[i]g[j]+g[j]2+g[j]+g[j]L+g[j]+g[j]L<f[k]g[i]g[k]g[i]g[k]+g[k]2+g[k]+g[k]L+g[k]+g[k]L

f [ j ] − f [ k ] < 2 g [ i ] ( g [ j ] − g [ k ] ) + g [ k ] 2 + 2 g [ k ] − g [ j ] 2 − 2 g [ j ] + 2 L ( g [ k ] − g [ j ] ) f[j]-f[k]<2g[i](g[j]-g[k])+g[k]^2+2g[k]-g[j]^2-2g[j]+2L(g[k]-g[j]) f[j]f[k]<2g[i](g[j]g[k])+g[k]2+2g[k]g[j]22g[j]+2L(g[k]g[j])

f [ j ] − f [ k ] < 2 ( g [ i ] − L − 1 ) ( g [ j ] − g [ k ] ) + g [ k ] 2 − g [ j ] 2 f[j]-f[k]<2(g[i]-L-1)(g[j]-g[k])+g[k]^2-g[j]^2 f[j]f[k]<2(g[i]L1)(g[j]g[k])+g[k]2g[j]2

f [ j ] − f [ k ] + g [ j ] 2 − g [ k ] 2 g [ j ] − g [ k ] < 2 ( g [ i ] − L − 1 ) \frac {f[j]-f[k]+g[j]^2-g[k]^2} {g[j]-g[k]}<2(g[i]-L-1) g[j]g[k]f[j]f[k]+g[j]2g[k]2<2(g[i]L1)

T [ i ] = f [ i ] + g [ i ] 2 T[i]=f[i]+g[i]^2 T[i]=f[i]+g[i]2,代入得:
T [ j ] − T [ k ] g [ j ] − g [ k ] < 2 ( g [ i ] − L − 1 ) \frac {T[j]-T[k]} {g[j]-g[k]} < 2(g[i]-L-1) g[j]g[k]T[j]T[k]<2(g[i]L1)

于是变成了一个斜率的形式了!那么斜率优化直接跑。

因为右边的 2 ( g [ i ] − L − 1 ) 2(g[i]-L-1) 2(g[i]L1) 具有单调性,所以可以用单调队列。

为了避免精度问题,我把上面的柿子中左边的除数移到了右边,这样就避免了浮点数运算。

代码如下:

#include <cstdio>
#include <cstring>
#define ll long long
#define maxn 50010

int n,L;
ll c[maxn],f[maxn],g[maxn],T[maxn];
int q[maxn],st=1,ed=1;
inline ll up(int x,int y){return T[x]-T[y];}
inline ll down(int x,int y){return g[x]-g[y];}
inline ll calc(int x,int now){return f[x]+(now-x-1+c[now]-c[x]-L)*(now-x-1+c[now]-c[x]-L);}

int main()
{
	scanf("%d %d",&n,&L);
	for(int i=1;i<=n;i++)
	scanf("%lld",&c[i]),c[i]+=c[i-1];
	q[st]=0;f[0]=0;
	for(int i=1;i<=n;i++)
	{
		g[i]=i+c[i];//注意g[i]的计算不能放到下面
		while(st<ed&&up(q[st+1],q[st])<2ll*(g[i]-(ll)L-1ll)*down(q[st+1],q[st]))st++;
		f[i]=calc(q[st],i); T[i]=f[i]+g[i]*g[i];
		while(st<ed&&up(i,q[ed])*down(q[ed],q[ed-1])<up(q[ed],q[ed-1])*down(i,q[ed]))ed--;
		q[++ed]=i;
	}
	printf("%lld",f[n]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值