HNOI2008玩具装箱TOY

原题

题目描述

P教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京。他使用自己的压缩器进行压缩,其可以将任意物品变成一堆,再放到一种特殊的一维容器中。P教授有编号为 1⋯N 的 N件玩具,第 i件玩具经过压缩后变成一维长度为 Ci .为了方便整理,P教授要求在一个一维容器中的玩具编号是连续的。同时如果一个一维容器中有多个玩具,那么两件玩具之间要加入一个单位长度的填充物,形式地说如果将第 i件玩具到第 j 个玩具放到一个容器中,那么容器的长度将为 X=j-i+S[j]-S[i-1] (其中S[i]为C[i]的前n项和)制作容器的费用与容器的长度有关,根据教授研究,如果容器长度为 X ,其制作费用为 (X-L)^2 .其中 L是一个常量。P教授不关心容器的数目,他可以制作出任意长度的容器,甚至超过 L 。但他希望费用最小.

输入输出格式

输入格式:

第一行输入两个整数N,L.接下来N行输入Ci.1<=N<=50000,1<=L,Ci<=10^7

输出格式:

输出最小费用

输入输出样例

输入样例#1:

1
2
3
4
5
6
5 4
3
4
2
1
4

输出样例#1:

1
1

思路

经典的斜率优化

前缀和sum[i]

显然dp方程为dp[i]=min(d[j]+(sum[i]-sum[j]+i-j-L-1)^2)其中j<i

然而明显超时

令a[i]=sum[i]+i

b[i]=sum[i]+i+L+1

dp[i]=dp[j]+(a[i]-b[j])^2

展开,有2 a[i] b[j]+dp[i]-a[i]^2= dp[j]+b[j]^2

将b[j]看作x dp[j]+b[j]^2看作y

这个式子就可以看作一条斜率为2*a[i]的直线

dp[i]的含义转化为 当上述直线过点(b[j],dp[j]+b[j]^2)时 直线在y轴的截距加上a[i]^2

题目要求找这个截距的最小值(线性规划问题)

最后再利用单调队列进行线性规划 问题解决

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <cstdio>
using namespace std;
int n,l;
int Q[50005];
double sum[50005],f[50005];
inline double a(int i){return sum[i]+i;}
inline double b(int i){return a(i)+l+1;}
inline double x(int i){return b(i);}
inline double y(int i){return f[i]+b(i)*b(i);}
inline double s(int i,int j){return (y(i)-y(j))/(x(i)-x(j));}
int main(){
scanf("%d%d",&n,&l);
for(int i=1;i<=n;++i){
scanf("%lf",&sum[i]);
sum[i]+=sum[i-1];
}
int head=1,tail=1;
for(int i=1;i<=n;++i){
while(head<tail&&s(Q[head],Q[head+1])<2*a(i))++head;
f[i]=f[Q[head]]+(a(i)-b(Q[head]))*(a(i)-b(Q[head]));
while(head<tail&&s(i,Q[tail-1])<s(Q[tail-1],Q[tail]))--tail;
Q[++tail]=i;
}
printf("%lld",(long long)f[n]);
return 0;
}