[HNOI2008] 玩具装箱

版权声明:全文无版权,目前博客已搬迁至https://bill.moe https://blog.csdn.net/Bill_Yang_2016/article/details/54671061

题目描述

  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。但他希望费用最小.


输入格式

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


输出格式

输出最小费用


样例数据

样例输入

5 4
3
4
2
1
4

样例输出

1


题目分析

根据题目易得O(n^2)方程
f[i]=min{f[j]+(i-j-1+s[i]-s[j]-L}^2} (1<=j< i)
然后TLE,怎么办呢?
这个方程将常数剔除出去,可能可以用单调队列,于是试一试6项平方展开?
换个元吧,令t[i]=i+s[i],z=L+1
f[i]=min{f[j]+(t[i]-z-t[j])^2}
=min{f[j]+t[j]^2-2t[i]t[j]+2t[j]z+(t[i]-z)^2}
=min{f[j]+t[j]^2-2t[i]t[j]+2t[j]z}+(t[i]-z)^2
然而中间有一个t[i]*t[j],不能用单调队列了,怎么办?
有没有觉得这个式子很眼熟?没错,斜率优化
移项,f[i]-(t[i]-z)^2=min{f[j]+t[j]^2-2t[i]t[j]+2t[j]z}}
令f[i]-(t[i]-z)^2=B,t[i]=k,x=2*t[j],y=f[j]+t[j]^2+2t[j]z
故B=-kx+y
kx+B=y
因为k=t[i]=i+s[i]随着i单调递增
故维护下凸包,完美解决。


源代码

#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<vector>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;
inline const int Get_Int() {
    int num=0,bj=1;
    char x=getchar();
    while(x<'0'||x>'9') {
        if(x=='-')bj=-1;
        x=getchar();
    }
    while(x>='0'&&x<='9') {
        num=num*10+x-'0';
        x=getchar();
    }
    return num*bj;
}
long long n,L,Q[50005],f[50005],sum[50005];
double Slope(long long i,long long j) { //求斜率
    return (double)((f[j]+(j+sum[j])*(j+sum[j])+2*(j+sum[j])*(L+1))-(f[i]+(i+sum[i])*(i+sum[i])+2*(i+sum[i])*(L+1)))/(2*(j+sum[j])-2*(i+sum[i]));
}
int main() {
    n=Get_Int();
    L=Get_Int();
    for(int i=1; i<=n; i++)sum[i]=sum[i-1]+Get_Int();
    int Left=1,Right=1;
    Q[1]=0;
    f[0]=0;
    for(int i=1; i<=n; i++) {
        while(Left<Right&&Slope(Q[Left],Q[Left+1])<=i+sum[i])Left++; //维护队首(删除非最优决策)
        int Front=Q[Left];
        f[i]=f[Front]+(i+sum[i]-L-1-Front-sum[Front])*(i+sum[i]-L-1-Front-sum[Front]); //计算当前f
        while(Left<Right&&Slope(Q[Right-1],Q[Right])>=Slope(Q[Right],i))Right--; //维护队尾(维护下凸包性质)
        Q[++Right]=i; //入队
    }
    printf("%lld\n",f[n]);
    return 0;
}

展开阅读全文

没有更多推荐了,返回首页