[HNOI2008]玩具装箱TOY

洛咕

题意:\(N(N<=50000)\)件玩具,每件玩具有一个长度\(c_i\),将玩具分成若干批,如果将第i件玩具到第j个玩具分作一批,那么该批的长度将为\(x=j-i+\sum_{k=i}^j c_k\),每批的代价为\((x-L)^2\),\(L\)是一个给定的常数,求最小代价和.

分析:设\(f[i]\)表示将前i件玩具分成若干批的最小代价,则\(f[i]=f[j]+(i-(j+1)+sum[i]-sum[j]-L)^2\)

假设\(k<j\)且j比k更优,即,

\(f[j]+(i-j-1+sum[i]-sum[j]-L)^2<f[k]+(i-k-1+sum[i]-sum[k]-L)^2\)

\(A[i]=i+sum[i]-L-1,g[i]=i+sum[i]\),则有

\(f[j]+(A[i]-g[j])^2<f[k]+(A[i]-g[k])^2\)

乱搞一下上式得到,

\(\frac{f[j]-g[j]^2-f[k]-g[k]^2}{g[j]-g[k]}<2A[i]\)

#include<bits/stdc++.h>
#define LL long long
using namespace std;
inline int read(){
   int s=0,w=1;char ch=getchar();
   while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
   while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
   return s*w;
}
const int N=50005;
int a[N],q[N];
LL sum[N],A[N],g[N],f[N];
int main(){
    int n=read(),L=read();
    for(int i=1;i<=n;i++){
		a[i]=read();
		sum[i]=sum[i-1]+a[i];
		A[i]=i+sum[i]-L-1;
		g[i]=i+sum[i];
    }
    int l=1,r=1;q[1]=0;
    memset(f,0x3f,sizeof(f));f[0]=0;
    for(int i=1;i<=n;i++){
		while(l<r&&(f[q[l+1]]-f[q[l]]+g[q[l+1]]*g[q[l+1]]-g[q[l]]*g[q[l]])<=(2*A[i]*(g[q[l+1]]-g[q[l]])))l++;
		f[i]=f[q[l]]+(A[i]-g[q[l]])*(A[i]-g[q[l]]);
		while(l<r&&(f[q[r]]-f[q[r-1]]+g[q[r]]*g[q[r]]-g[q[r-1]]*g[q[r-1]])*(g[i]-g[q[r]])>=(f[i]-f[q[r]]+g[i]*g[i]-g[q[r]]*g[q[r]])*(g[q[r]]-g[q[r-1]]))r--;
		q[++r]=i;
    }
    printf("%lld\n",f[n]);
    return 0;
}

posted on 2019-06-12 08:24  PPXppx  阅读(87)  评论(0编辑  收藏  举报