BZOJ1010玩具裝箱Toy

BZOJ1010玩具裝箱Toy

Description

P教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京。他使用自己的压缩器进行压
缩,其可以将任意物品变成一堆,再放到一种特殊的一维容器中. P教授有编号为 1..N N 件玩具,第i件玩具经过压缩后变成一维长度为 Ci .为了方便整理, P教授要求在一个一维容器中的玩具编号是连续的。同时如果一个一维容
器中有多个玩具,那么两件玩具之间要加入一个单位长度的填充物,形式地说如果将第i件玩具到第j个玩具放到一个容器中,那么容器的长度将为

x=ji+k=ijCi
制作容器的费用与容器的长度有关,根据教授研究,如果容器长度为 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

典型的斜率優化DP
f[i] 記錄標號為從 1 i的玩具全部壓縮好所需要的最小費用, 則可以得到DP递推式

f[i]=mini1j=0(f[j]+(sum[i]sum[j]+ij1L)2)
其中 sum[i] 記錄編號從 1 i的玩具的長度總和.
假設在 i>j>k 中, 對於 i j k 優, 則
f[j]+(sum[i]sum[j]+ij1L)2<f[k]+(sum[i]sum[k]+ik1L)2

化簡得到
(f[j]+b[j]2)(f[k]+b[k]2)b[j]b[k]<2a[i]

其中
a[i]=sum[i]+1L
b[i]=sum[i]+i

*Hint: 化簡有一定技巧. 一般來說, 化簡得到的結果要把含 i 的項移至等號右邊, 不含i的項移至左邊, 以方便後續運算.

可以將這個除法式子理解為所謂的斜率, 記為 slope(j,k)

然後用隊列來維護DP. 具體過程如下:
1. 對於隊頭的兩個元素, 假如有 slope(queue[head+1],queue[head])>2a[i] 則說明隊列中第二個元素比第一個優, 隊頭出隊.
2. 此時可以確保隊頭元素是最優解, 用隊頭元素計算出 f[i] 的數值
3. 在有了 f[i] 的值的情況下, 就可以在隊尾進行維護了. 對於隊尾的兩個元素記為 x,y(x>y) , 假如有 slope(x,y)>slope(i,x) 則說明 x 是無用的, 可以出隊. 具體證明如下: (1). 假如slope(x,y)>slope(i,x)>2a[i], 則雖然有 x i優, 但又有 y x優, 因此 x 可出隊; (2). 假如slope(x,y)>slope(i,x)<2a[i], 則有 i x優, x 可出隊.
4. 將i加入隊尾

維護過程結束. 注意每一步的順序, 都是有先後性的, 不要搞反.
然後再說道一個點, 這一題一定要開 longlong
感覺現階段推公式的能力還要加強, 這一坨東西我推錯了好多次QAQ
附上代碼

#include<cstdio>
#include<cctype>
#include<cstring>
using namespace std;
inline long long read()
{
    long long x = 0, flag = 1;
    char c;
    while(! isdigit(c = getchar()))
        if(c == '-')
            flag *= - 1;
    while(isdigit(c))
        x = x * 10 + c - '0', c = getchar();
    return x * flag;
}
const long long N = 1 << 16;
long long c[N];
long long sum[N];
long long a[N], b[N];
long long queue[N];
long long f[N];
inline long long sqr(long long x)
{
    return x * x;
}
inline long double slope(long long x, long long y)
{
    return (long double)((f[x] + sqr(b[x])) - (f[y] + sqr(b[y]))) / (long double)(b[x] - b[y]);
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("BZOJ1010.in", "r", stdin);
    freopen("BZOJ1010.out", "w", stdout);
    #endif
    long long n = read(), l = read();
    sum[0] = 0;
    a[0] = - 1 - l;
    b[0] = 0;
    for(long long i = 1; i <= n; i ++)
    {
        c[i] = read();
        sum[i] = c[i] + sum[i - 1];
        a[i] = sum[i] + i - 1 - l;
        b[i] = sum[i] + i;
    }
    long long head = 0, tail = 1;
    queue[head] = 0; 
    memset(f, 0, sizeof(f));
    for(long long i = 1; i <= n; i ++)
    {
        while(head + 1 < tail && slope(queue[head + 1], queue[head]) < 2 * (float)a[i])
            head ++;
        f[i] = f[queue[head]] + sqr(a[i] - b[queue[head]]);
        while(head + 1 < tail && slope(queue[tail - 1], queue[tail - 2]) > slope(i, queue[tail - 1]))
            tail --;
        queue[tail ++] = i;
    }
    printf("%lld\n", f[n]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值