[ HNOI 2008 ] 玩具装箱

\(\\\)

Description


现有编号为 \(1\sim N\)\(N\) 件玩具,第 \(i\) 件长度为 \(C_i\)

现在要把按顺序排好的玩具划分成若干段,$[l,r] $的长度是 \(len_{\ l\sim r}=r-l+\sum_{i=l}^r C_i\)

一段的代价为 \((len-L)^2\) ,其中 \(L\) 是给出的常量。

求最小总代价。

  • \(N\le 5\times10^4,C_i,L\le 10^9\)

\(\\\)

Solution


\(len(l,r)\) 表示区间 \((l,r]\) 新成一段的最后长度。

显然有 \(len(l,r)=r-l-1+\sum_{i=l+1}^rC_i\)

记录 \(sum[i]=\sum_{j=1}^iC_j\) ,那么有

\[len(l,r)=r+sum[r]-(l+sum[l])-1 \]

发现式子可以表示成 \(l,r\) 相关,我们设 \(t(i)=i+sum[i]\) ,那么有

\[len(l,r)=t(r)-t(l)-1 \]

我们设 \(w(l,r)\) 表示区间 \((l,r]\) 新生成一段的代价,那么有

\[w(l,r)=\bigg(len(l,r)-L\bigg)^2=\bigg(t(r)-t(l)-1-L\bigg)^2 \]

不妨再设 \(g(i)=t(i)+1+L\) ,那么有 \(w(l,r)=\bigg(t(r)-g(l)\bigg)^2=t(r)^2+g(l)^2-2g(l)t(r)\)

显然 \(t\)\(g\) 是可以 \(O(n)\) 预处理的,询问可以做到 \(O(1)\)

\(\\\)

\(f[i]\) 表示到第 \(i\) 个玩具为止 \((\) 含第 \(i\)\()\),全部装箱的最小代价。

\[f[i]=\min_{j=1}^{i-1}\{f[j]+w(j,i)\}=\min_{j=1}^{i-1}\{f[j]+t(i)^2+g(j)^2-2g(j)t(i)\} \]

假设我们现在找到了最优转移点 \(j\) ,那么有

\[f[i]=f[j]+t(i)^2+g(j)^2-2g(j)t(i) \]

移项,有

\[f[j]+g(j)^2=f[i]-t(i)^2+2t(i)g(j) \]

此时我们可以将转移模型抽象到二维平面了,设点 \((g(j),f[j]+g(j)^2)\) 表示一个状态 。

我们将 \(2t(i)\) 看作斜率,将 \(f[i]-t(i)^2\) 看作与 \(y\) 轴相交点的纵坐标。

目标明确了:选择之前在坐标系上的任意一点用斜率构建直线,使得这一纵坐标最小。

\(\\\)

因为 \(C_i\ge 1\) 所以 \(t(i)\ge 0\) ,我们的答案显然只会产生在下凸壳上。

如果我们用 \(j\) 去刻画 \(f[j]\) 对应的状态,考虑对于一个状态 \(i\) 的更新,\(j\) 点的答案合适劣于 \(k\) 点的答案。

移项,把 \(j,k\) 的答案都带进去,列出不等式,化简,可以得到 \((\) 博主太懒 \():\)

\[f[k]+g(k)^2-2g(k)t(i)\le f[j]+g(j)^2-2g(j)t(i)\\ \frac{f[k]+g(k)^2-f[j]-g(j)^2}{g(k)-g(j)}\le 2t(i) \]

继续分析,还是因为 \(C_i\ge 1\) ,所以 \(t(i)\) 是单调不降的。

因此注意到不等式右侧是单调不降的,所以每一个位置的最优决策点 \(j\) 单调不减。

用队列维护点,队头用单调队列那一套就可以搞了。

关于下凸包的维护,可以考虑队尾是一个单调栈,每次按照斜率逐个弹栈就可以了。

\(\\\)

特别提醒:此做法中 \(g(0)\) 初值不为 \(0\) ,而转移要用到,在开始的时候记得设 \(g(0)=1+L\)

其实设 \(g(i)=i+m,t(i)=g(i)-1-L\) ,可以惊喜的发现,后面所有的式子都是不变的。

而且也不需要预处理初值,因为转移方程在没有 \(t(j)\) 这一项,而在这一设法中, \(g(0)=0\)

\(\\\)

Code


#include<cmath>
#include<cctype>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 50010
#define R register
#define gc getchar
using namespace std;
typedef long long ll;

inline ll rd(){
  ll x=0; bool f=0; char c=gc();
  while(!isdigit(c)){if(c=='-')f=1;c=gc();}
  while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
  return f?-x:x;
}

ll n,m,t[N],g[N],f[N],q[N],hd,tl;

inline ll w(ll x,ll k){
  return f[x]+k*k+g[x]*g[x]-2*k*g[x];
}

inline double calck(ll x,ll y){
  return (double)(f[x]+g[x]*g[x]-f[y]-g[y]*g[y])/(double)(g[x]-g[y]);
}

int main(){
  n=rd(); m=rd();
  for(R int i=1,sum=0;i<=n;++i){
    sum+=rd();
    t[i]=i+sum; g[i]=t[i]+1+m;
  }
  q[hd=tl=1]=0;
  g[0]=1+m;
  for(R int i=1;i<=n;++i){
    while(hd<tl&&calck(q[hd+1],q[hd])<(double)t[i]*2) ++hd;
    f[i]=w(q[hd],t[i]);
    while(hd<tl&&calck(q[tl],q[tl-1])>calck(i,q[tl])) --tl;
    q[++tl]=i;
  }
  printf("%lld\n",f[n]);
  return 0;
}
posted @ 2018-11-12 20:59  SGCollin  阅读(181)  评论(0编辑  收藏  举报