1116. 【HNOI2008】T_OY(踢欧阳^_^) (Standard IO)
Time Limits: 1000 ms Memory Limits: 65536 KB Detailed Limits
Description
8月P教授要去看奥运,但是他割舍不下自己的一大堆智力玩具。于是,他决定把所有玩具都运到北京去。P教授使用自己的物体维数压缩器ODZ(Object Dimension Zipper)来给玩具装箱。ODZ 可以将任意物品变成一维,再装到一种特殊的一维容器中。P教授有编号为1..N的N件玩具,第i件玩具经过ODZ处理后一维长度是Ci。为了方便整理,P教授要求在一个一维容器中的玩具编号是连续的。同时,如果一个一维容器中有多个玩具,那么相信两件玩具之间要加入1个单位长度的填充物。
形式地说,如果将第i到第j件玩具放在一个容器中,那容器的长度将为:
x=j-i+sigma(Ck) //i<=k<=j
制作容器的费用与容器长度有关。根据P教授的研究,如果容器长度为x,其制作费用为(x-L)^2,其中L是一个常量。P教授不关心容器的数目,他可以制造出任意长度的容器(甚至超过L),但他希望费用最小。
Input
第一行输入两个整数N和L,接下来N行输入Ci。1<=N<=50000,1<=L,Ci<=10^7。
Output
输出最小费用。
Sample Input
5 4
3
4
2
1
4
Sample Output
1
Data Constraint
Source / Author: HNOI2008第二试第三题 TOY
题解:
原来的想法:设f[i] 为1~i放完最小代价。
假设j+1~i分一组
f[i] = max(f[j] + (i - j - 1 + pre[i] - pre[j] - L)^2)
pre是前缀和。
考虑优化:
我们把平方的项,设为O,进行整理:
O = (i - j - 1 + pre[i] - pre[j] - L)^2
= [(i+pre[i] - L -1)- (j+pre[j]) ]^2
= (i+pre[i] - L -1)^2 + (j+pre[j])^2
- 2(i+pre[i] - L -1 (j+pre[j])
若i处决策点a优于b ,b<a
则
f[a] +(i+pre[i] - L -1)^2 + (a+pre[a])^2
- 2(i+pre[i] - L -1)*(a+pre[a])
<
f[b] + (i+pre[i] - L -1)^2 +
- 2(i+pre[i] - L -1)*(b+pre[b])
所以
f[a]+(a+pre[a])^2 - (b+pre[b])^2
<
f[b]+2(i+pre[i] - L -1)*(a+pre[a])- 2(i+pre[i] - L -1)*(b+pre[b])
也就是
<
f[b]+2(i+pre[i] - L -1)( a+pre[a]-b-pre[b])
【f[a]+(a+pre[a])^2】-【f[b]+(b+pre[b])^2】
—————————————————————————————— < 2(i+pre[i] - L -1)
a+pre[a]-b-pre[b]
像极了斜率。
拆式子套路:关于ab的放在左边,i和常数到右边。
我们把不等式右边设为R,左边的设为s(b,a)表示ab的斜率,若s(b,a) < R,则a更优。
于是我们发现重要结论: 三个决策点a,b,c 若s(c,b) >s(b,a) 则 删除决策点b。(c < b < a)
证明:
若R < s(b,a) < s(c,b) 则 b优于a 且 c优于b ,最优为c
若 s(b,a) < R < s(c,b) 则a优于b , c优于b , 最优不为b
若 s(b,a) < s(c,b) < R , 则a优于b,且 b优于c,最优为a
若s(c,b) >s(b,a) 则 删除决策点b。
b无容身之地,从此踢掉就可以了。
维护一个队列,每次加点操作,我们都往前删。
发现我们维护了一个下凸包,斜率越来越大,满足单调性。
此图A为新加入点。
随着i增大,R单调上升。
单调队列,越往队头斜率越小。
所以s(F,D) < S(D,C);
同上,枚举R的大小位置,可以得到:
若S(F,D) < R 删除F
我们这样类推往从头往后删。
删完点,所有的线段斜率都大于R了。
S(D,C) >R ; S(C,A) > R;
D优于C,C优于A。。。
得到此时队头这个决策点最优!
#include<cstdio>
#include<cmath>
#define N 50010
#define ll long long
#define open(x) freopen(x".in","r",stdin);freopen(x".out","w",stdout);
using namespace std;
ll n,L,i,j,t,ans;
ll pre[N],g[N],f[N],c[N],tail,head;
double q[N];
double sqr(double x){return x*x;}
double slope(ll x,ll y)
{
return ( f[x] + sqr(g[x]) - f[y]- sqr(g[y]) ) / (g[x] - g[y]);
}
int main()
{
scanf("%lld%lld",&n,&L);
for(i=1;i<=n;i++)scanf("%lld",&c[i]);
for(i=1;i<=n;i++) pre[i] = pre[i-1] + c[i],g[i] = i + pre[i];
head = tail=1;
for(i=1;i<=n;i++)
{
while(head < tail && slope(q[head],q[head+1]) <= 2*(g[i] - L -1))++head;
j=q[head];
f[i] = f[j] + sqr(g[i] - g[j] -1 - L);
while(head<tail && slope(q[tail-1],q[tail]) >= slope(q[tail],i))--tail;
q[++tail] = i;
}
printf("%lld",f[n]);
return 0;
}