[luogu P3195] [HNOI2008]玩具装箱TOY

时间:2022-04-02 13:22:44

[luogu P3195] [HNOI2008]玩具装箱TOY

题目描述

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

输出格式:

输出最小费用

输入输出样例

输入样例#1:

5 4

3

4

2

1

4

输出样例#1:

1

开启斜率优化DP的学习。(可能因为状态原因,学的比较慢)

不太会讲理论,就对这道题讲一讲吧。

首先,我们可以列出一个n^2的转移方程:

f[i]=min(f[j]+(i-j-1+(s[i]-s[j])-L)^2)(j<i)。

将这个式子变化一下:

f[i]=min(f[j]+((i+s[i]-L-1)-(j+s[j]))^2)

设a[i]=i+s[i]-L-1,b[i]=i+s[i]。(当然也可以用一个数组,这里为了方便分辨)

则f[i]=min(f[j]+(a[i]-b[j])^2)=min(f[j]+a[i]^2-2a[i]b[j]+b[j]^2)。

即f[i]=?f[j]+a[i]^2-2a[i]b[j]+b[j]^2。

假装这是成立的,则设y=f[j]+b[j]^2,k=2a[i],x=b[j],b=f[i]-a[i]^2,

得到y=kx+b。

显然,我们需要让f[i]尽可能的小,就需要使得f[i]=b+a[i]^2尽可能小,也就是使b尽可能的小。

由于b=y-kx,其中k是确定的,y和x对于每一个j<i都是确定的,我们就可以将(x,y)表示在二维平面上。

现在,假如我们固定住x坐标,则y坐标要尽可能的小。

如果固定y坐标,则要尽可能增大x=b[j],根据前面的定义,b[]是单调递增的。但是由于y也是单调递增的,所以问题变得复杂了些。

那怎么办?这就涉及到了问题的本质。

对于一类转移方程:

f[i]=min{a[i]*b[j]+c[i]+d[j]}(a[i]和c[i]是常数,b[j]和d[j]与f[j]相关,j<i)

可以使用斜率优化。

稍微转换一下,考虑两个转移j和k,假如j比k更优,有a[i]*b[j]+c[i]+d[j]<a[i]*b[k]+c[i]+d[k]。

为方便考虑,设bj<bk,则得到-a[i]<(d[k]-d[j])/(b[k]-b[j]),这后面的就可以看成两点间直线的斜率。

那这就提示了我们,我们可以在两点间的斜率上做点功夫。

对于这题,沿用上面的式子,得到2a[i]<k(u,v)时,u比v更优。k(u,v)=(yv-yu)/(xv-xu)

这样,我们相当于要维护一个斜率直升不降的单调队列,每次询问时找出满足k(head,head+1)>2a[i]的head,然后,将更新后的f[i]插入到单调队列的队尾。

这样,我们就能将时间复杂度降到O(N)了。(个人觉得有点凌乱,如果读者觉得有上面地方表达不妥或有误,望指出)

code:

 %:pragma GCC optimize()
 #include<bits/stdc++.h>
 #define sqr(x) ((x)*(x))
 #define LL long long
 using namespace std;
 ;
 const double inf=1e18;
 int n,L,l,r; LL f[N],c[N],a[N],b[N];
 struct point {
     LL x,y;
     point() {}
     point(LL _x,LL _y):x(_x),y(_y) {}
 }st[N];
 inline int read() {
     ; char ch=getchar();
     ') ch=getchar();
     +ch-',ch=getchar();
     return x;
 }
 double slope(point u,point v) {
     return u.x==v.x?(u.y<v.y?inf:-inf):1.0*(v.y-u.y)/(v.x-u.x);
 }
 LL get(LL k) {
     ])<1.0*k) l++;
     return st[l].y-k*st[l].x;
 }
 void insert(point cur) {
     ],st[r])>slope(st[r-],cur)) r--;
     st[++r]=cur;
 }
 int main() {
     n=read(),L=read(),c[]=;
     ; i<=n; i++) c[i]=c[i-]+read();
     ; i<=n; i++) a[i]=i+c[i]-L-;
     ; i<=n; i++) b[i]=i+c[i];
     memset(f,,sizeof f);
     l=,r=,st[++r]=point(,);
     ,j; i<=n; i++) {
         f[i]=*a[i])+sqr(a[i]);
         insert(point(b[i],f[i]+b[i]*b[i]));
     }
     printf("%lld\n",f[n]);
     ;
 }