[HNOI2008]玩具装箱TOY(斜率优化)

[HNOI2018]玩具装箱TOY

这道题需要用到斜率优化,不会的同学点此

题目描述

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

这道题目是一道很好的斜率优化入门题,状态转移方程应该不难得出

\[dp[i]=min(dp[j]+(sum[i]+i-sum[j]-j-L-1)^2) (j<i) \]

\(sum[i]\) 为前缀和,表示放入前i件物品所占的长度。

\[a[i]=sum[i]+i-L-1 \]

\[b[j]=sum[j]+j \]

那么此时dp方程可转化为

\[dp[i]=dp[j]+(a[i]-b[j])^2 (j<i) \]

进一步化简

\[dp[i]=dp[j]+a[i]^2-2*a[i]*b[j]+b[j]^2 \]

移项,得

\[dp[j]+b[j]^2=2a[i]*b[j]+dp[i]-a[i]^2 \]

如果你学会了斜率优化,到这一步就很明了了
\(b[j]\)看作\(x\)\(dp[j]+b[j]^2\)看作\(y\),斜率为\(2a[i]\)的直线
\(\because\) \(a[i]\) 单调递增
\(\therefore\) 我们需要维护一个下凸包
\(\because\) \(b[j]\)也单调递增
\(\therefore\) 用队列维护即可

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#define ll long long
using namespace std;
ll read()
{
    ll x=0,w=1;char ch=getchar();
    while(ch>'9'||ch<'0') {if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return x*w;
}
ll dp[50010],sum[50010];
ll team[50010];
long long get(int x)
{
    return dp[x]+(x+sum[x])*(x+sum[x]);
}
int main()
{
    int n=read(),lll=read(),a;
    for(int i=1;i<=n;i++)
    {
        a=read();
        sum[i]=sum[i-1]+a;
    }
    dp[0]=0;
    int l=1,r=1;
    for(int i=1;i<=n;i++)
    {
        ll qwe=i+sum[i]-lll-1;
        while(l<r&&get(team[l+1])-get(team[l])<=2*qwe*(team[l+1]-team[l]+sum[team[l+1]]-sum[team[l]])) l++;
        dp[i]=get(team[l])-2*qwe*(team[l]+sum[team[l]])+qwe*qwe;
        while(l<r&& (get(team[r])-get(team[r-1])) * (i+sum[i]-team[r]-sum[team[r]]) >= (get(i)-get(team[r])) * (team[r]+sum[team[r]]-team[r-1]-sum[team[r-1]]) )
        r--;
        team[++r]=i;
        /*for(int j=0;j<i;j++)
        {
            if(dp[i]>dp[j]+(i-j-1+sum[i]-sum[j]-l)*(i-j-1+sum[i]-sum[j]-l))
            {
                dp[i]=dp[j]+(i-j-1+sum[i]-sum[j]-l)*(i-j-1+sum[i]-sum[j]-l);
            }
        }*/
    }
    cout<<dp[n];
}
posted @ 2018-05-20 19:44  Frozen_Heart  阅读(145)  评论(0编辑  收藏  举报