bzoj1010 [HNOI2008]玩具装箱toy(斜率)

来源:互联网 发布:巨人网络最新游戏 编辑:程序博客网 时间:2024/05/16 11:07

Description
P教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京。他使用自己的压缩器进行压
缩,其可以将任意物品变成一堆,再放到一种特殊的一维容器中。P教授有编号为1…N的N件玩具,第i件玩具经过
压缩后变成一维长度为Ci.为了方便整理,P教授要求在一个一维容器中的玩具编号是连续的。同时如果一个一维容
器中有多个玩具,那么两件玩具之间要加入一个单位长度的填充物,形式地说如果将第i件玩具到第j个玩具放到一
个容器中,那么容器的长度将为 x=j-i+Sigma(Ck) i<=K<=j 制作容器的费用与容器的长度有关,根据教授研究,
如果容器长度为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

分析:
斜率优化dp

这道题不限箱子的数目,所以一维就够了(终于是一维了,受不了二维)
f[i]=f[j]+((i-j-1+sum[i]-sum[j])-L)^2

斜率优化

画柿子演示:
设k < j < i,转移点j比k更优
则有:
这里写图片描述

斜率的形式我们有了
g[j][k] < i+sum[i]
表示j比k优

关键点
g[i][j] < g[j][k]
j点不可能作为转移点

主要操作:
1.维护一个双端对列
2.在转移的时候,如果队列中依次有a,b,c等元素
若g[b][a] < sum[i]+i,a出队
直到g[x][y]>=sum[i]+i
从队首转移
3.入队的时候,如果队列中依次有a,b,c等元素,入队元素为d
若g[d][c] < g[c][b] ,c出对
直到g[d][x]>=g[x][y] ,d入队

tip

不用特殊计算f[1]的值,
直接在循环里维护就行了(这样既可以维护f,也可以入队)

我这个zz
在写转移方程的时候竟然忘了累加

f[i]=f[q[tou]]+sqr(i-q[tou]+sum[i]-sum[q[tou]]-L);

我发现只要是牵扯到L,用到的都是L+1,所以我干脆在一开始的时候就L++

这里写代码片#include<cstdio>#include<cstring>#include<iostream>#include<cmath>#define ll long longusing namespace std;const int N=50010;int n,q[N],tou,wei;ll l,a[N],f[N];ll sqr(ll x){    return x*x;}double get(int j,int k){    double x1=(double)f[j]+sqr(j+a[j])+2*l*(j+a[j]);    double x2=(double)f[k]+sqr(k+a[k])+2*l*(k+a[k]);    double x3=(double)2*(j+a[j]-k-a[k]);    return (double)(x1-x2)/x3;}void doit(){    int i,j;    tou=wei=0;    for (i=1;i<=n;i++)    {        while (tou<wei&&get(q[tou+1],q[tou])<=i+a[i]) tou++;        f[i]=f[q[tou]]+sqr((i-q[tou]+a[i]-a[q[tou]])-l);        while (tou<wei&&get(i,q[wei])<=get(q[wei],q[wei-1])) wei--;        q[++wei]=i;    }    printf("%lld",f[n]);}int main(){    scanf("%d%lld",&n,&l);    l++;    for (int i=1;i<=n;i++) scanf("%lld",&a[i]),a[i]+=a[i-1];    doit();    return 0;}
阅读全文
0 0