1. 程式人生 > >LG3195 [HNOI2008]玩具裝箱TOY

LG3195 [HNOI2008]玩具裝箱TOY

題意

P教授要去看奧運,但是他舍不下他的玩具,於是他決定把所有的玩具運到北京。他使用自己的壓縮器進行壓縮,其可以將任意物品變成一堆,再放到一種特殊的一維容器中。P教授有編號為\(1\cdots N\)\(N\)件玩具,第\(i\)件玩具經過壓縮後變成一維長度\(C_i\).為了方便整理,P教授要求在一個一維容器中的玩具編號是連續的。同時如果一個一維容器中有多個玩具,那麼兩件玩具之間要加入一個單位長度的填充物,形式地說如果將第\(i\)件玩具到第\(j\)個玩具放到一個容器中,那麼容器的長度將為\(x=j-i+\sum\limits_{k=i}^{j}C_k\)製作容器的費用與容器的長度有關,根據教授研究,如果容器長度為\(x\)

,其製作費用為\((X-L)^2\).其中\(L\)是一個常量。P教授不關心容器的數目,他可以製作出任意長度的容器,甚至超過\(L\)。但他希望費用最小.

\(N \leq 50000\)

分析

明顯的dp,\(dp[i]\)表示前\(i\)個的最小花費。
\[ dp[i]=\min_{j<i} \{ dp[j]+(i-j-1+c[i]-c[j]-L)^2 \} \]
其中\(c\)表示字首和。

然後呢?拆開?

其實我開始是這麼想的。然後看了一下題解,發現可以發揮資訊學的優勢。令\(a[i]=i+c[i]-L-1,b[i]=c[i]+i\),那麼就很簡單了。

\(j>k\),且\(j\)

\(k\)優,則
\[ \frac{dp[j]+b[j]^2-dp[k]-b[k]^2}{b[j]-b[k]}<2a[i] \]
小於單調增,下凸包+單調佇列。

時間複雜度\(O(n)\)

程式碼

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<algorithm>
#include<bitset>
#include<cassert>
#include<ctime>
#include<cstring>
#define rg register
#define il inline
#define co const
template<class T>il T read()
{
    rg T data=0;
    rg int w=1;
    rg char ch=getchar();
    while(!isdigit(ch))
    {
        if(ch=='-')
            w=-1;
        ch=getchar();
    }
    while(isdigit(ch))
    {
        data=data*10+ch-'0';
        ch=getchar();
    }
    return data*w;
}
template<class T>T read(T&x)
{
    return x=read<T>();
}
using namespace std;
typedef long long ll;

co int N=5e4+2;
ll c[N];
ll a[N],b[N];
ll dp[N];

ll Up(int j,int k)
{
    return dp[j]+b[j]*b[j]-dp[k]-b[k]*b[k];
}

ll Down(int j,int k)
{
    return b[j]-b[k];
}

ll Cal(int i,int j)
{
    return dp[j]+a[i]*a[i]-2*a[i]*b[j]+b[j]*b[j];
}

int q[N];

int main()
{
//  freopen(".in","r",stdin);
//  freopen(".out","w",stdout);
    int n=read<int>(),L=read<int>();
    for(int i=1;i<=n;++i)
    {
        c[i]=c[i-1]+read<int>();
        a[i]=i+c[i]-L-1;
        b[i]=c[i]+i;
    }
    int head=0,tail=0;
    q[tail++]=0;
    for(int i=1;i<=n;++i)
    {
        while(head+1<tail&&Up(q[head+1],q[head])<=2*a[i]*Down(q[head+1],q[head]))
            ++head;
        dp[i]=Cal(i,q[head]);
        while(head+1<tail&&Up(i,q[tail-1])*Down(q[tail-1],q[tail-2])<=Up(q[tail-1],q[tail-2])*Down(i,q[tail-1]))
            --tail;
        q[tail++]=i;
    }
    printf("%lld\n",dp[n]);
    return 0;
}