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