玩具装箱 [HNOI2008] (NLOGN)

69 篇文章 0 订阅
1 篇文章 0 订阅

题目地址请点击——


Toy


Description

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


Input

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


Output

输出最小费用


Sample Input

5 4
3
4
2
1
4


Sample Output

1


Solution

f(x) 表示前 x 个玩具的最小费用。

f(x)=minx1i=1{f(i)+w[i+1,x]}
其中

w[i,j]=(ji+k=1jc[k]L)2

打表发现满足决策单调性,优化到 O(nLogn)


Code

#include <iostream>
#include <cstdio>

#define LL long long
#define k s[end][0]
#define tpm (s[top][1]-i-1+c[s[top][1]]-c[i]-l)
#define mpt (s[top][1]-s[top][0]-1+c[s[top][1]]-c[s[top][0]]-l)
#define tpm2 (s[top-1][1]-i-1+c[s[top-1][1]]-c[i]-l)
#define mpt2 (s[top-1][1]-s[top-1][0]-1+c[s[top-1][1]]-c[s[top-1][0]]-l)
#define Max(x,y) ((x)>(y)?(x):(y))
#define Min(x,y) ((x)<(y)?(x):(y))

using namespace std;

LL n,l,top=0,end=0;
LL c[50010];
LL s[50010][3];
LL f[50010];

bool judge(LL x){
    LL now=s[top][0];
    LL pre=s[top-1][0];
    return (f[now]+(x-now-1+c[x]-c[now]-l)*(x-now-1+c[x]-c[now]-l)<=f[pre]+(x-pre-1+c[x]-c[pre]-l)*(x-pre-1+c[x]-c[pre]-l));
}

LL find(LL l,LL r){
    LL ans=2147483647;
    while(l<=r){
        LL mid=(l+r)/2;
        if(judge(mid)){
            ans=Min(ans,mid);
            if(r!=mid-1)r=mid-1;
            else break;
        }
        else{
            if(l!=mid+1)l=mid+1;
            else break;
        }
    }
    return ans;
}

int main(){

    scanf("%lld%lld",&n,&l);

    for(LL i=1;i<=n;i++){
        LL x;
        scanf("%lld",&x);
        c[i]=c[i-1]+x;
    }

    f[0]=0;

    end=1;
    s[++top][0]=0;s[top][1]=1;s[top][2]=n;

    for(LL i=1;i<=n;i++){

        while(i>s[end][2])end++;
        LL tmp=(i-k-1+c[i]-c[k]-l);

        f[i]=f[k]+tmp*tmp;

        if(s[top][1]>i&&f[i]+tpm*tpm<=f[s[top][0]]+mpt*mpt){
            s[top][0]=i;
            while(top>1&&f[i]+tpm2*tpm2<=f[s[top-1][0]]+mpt2*mpt2){
                s[--top][0]=i;
                s[top][2]=s[top+1][2];
            }
            if(top>1)s[top-1][2]=s[top][1]-1;
        }
        else{
            s[++top][0]=i;
            s[top][2]=n;
        }

        if(top>1){
            s[top][1]=find(Max(i+1,s[top-1][1]+1),n);
            s[top-1][2]=s[top][1]-1;
        }
    }

    printf("%lld\n",f[n]);

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值