洛谷P3195 [HNOI2008] 玩具装箱
题目描述
P 教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京。他使用自己的压缩器进行压缩,其可以将任意物品变成一堆,再放到一种特殊的一维容器中。
P 教授有编号为 1⋯n1 \cdots n1⋯n 的 nnn 件玩具,第 iii 件玩具经过压缩后的一维长度为 CiC_iCi。
为了方便整理,P 教授要求:
-
在一个一维容器中的玩具编号是连续的。
-
同时如果一个一维容器中有多个玩具,那么两件玩具之间要加入一个单位长度的填充物。形式地说,如果将第 iii 件玩具到第 jjj 个玩具放到一个容器中,那么容器的长度将为 x=j−i+∑k=ijCkx=j-i+\sum\limits_{k=i}^{j}C_kx=j−i+k=i∑jCk。
制作容器的费用与容器的长度有关,根据教授研究,如果容器长度为 xxx,其制作费用为 (x−L)2(x-L)^2(x−L)2。其中 LLL 是一个常量。P 教授不关心容器的数目,他可以制作出任意长度的容器,甚至超过 LLL。但他希望所有容器的总费用最小。
输入格式
第一行有两个整数,用一个空格隔开,分别代表 nnn 和 LLL。
第 222 到 第 (n+1)(n + 1)(n+1) 行,每行一个整数,第 (i+1)(i + 1)(i+1) 行的整数代表第 iii 件玩具的长度 CiC_iCi。
输出格式
输出一行一个整数,代表所有容器的总费用最小是多少。
样例 #1
样例输入 #1
5 4
3
4
2
1
4
样例输出 #1
1
提示
对于全部的测试点,1≤n≤5×1041 \leq n \leq 5 \times 10^41≤n≤5×104,1≤L≤1071 \leq L \leq 10^71≤L≤107,1≤Ci≤1071 \leq C_i \leq 10^71≤Ci≤107。
问题介绍
有的题目的转移方程并不如我们所预期的样式,而是形如dpi=max/min(a[i]⋅b[j]+c[i]+d[j])dp_i=max/min(a[i]\cdot b[j]+c[i]+d[j])dpi=max/min(a[i]⋅b[j]+c[i]+d[j])的形式,单调队列优化不再成立,我们需要找到新的优化方式解决。
以玩具装箱的背景理解斜率优化
式子处理
因为有∑Ci\sum C_i∑Ci的存在,所以前缀和是必然的,特别注意的一点就是我们不妨将填充物的长度计入物体长度,计算总和时减111即可。即令Si=∑i=1n(Ci+1)S _i=\sum\limits_{i=1}^{n}(C_i+1)Si=i=1∑n(Ci+1)。
设dpidp_idpi表示装好前iii个玩具的最小花费,则有dpi=minj∈[0,i−1](dpj+(Si−Sj−1−L)2)dp_i=\mathop{min}\limits_{j\in[0,i-1]}(dp_j+(S_i-S_j-1-L)^2)dpi=j∈[0,i−1]min(dpj+(Si−Sj−1−L)2)
方便拆括号,于是可以令L′=L+1L'=L+1L′=L+1,则dpi=minj∈[0,i−1](dpj+(Si−Sj−L′)2)dp_i=\mathop{min}\limits_{j\in[0,i-1]}(dp_j+(S_i-S_j-L')^2)dpi=j∈[0,i−1]min(dpj+(Si−Sj−L′)2)
方便接下来化简,我们写作dpi=minj∈[0,i−1](dpi,j),dpi,j=dpj+(Si−Sj−L′)2dp_i=\mathop{min}\limits_{j\in[0,i-1]}(dp_{i,j}),dp_{i,j}=dp_j+(S_i-S_j-L')^2dpi=j∈[0,i−1]min(dpi,j),dpi,j=dpj+(Si−Sj−L′)2
注意到在dpidp_idpi的更新中,iii始终不变,在转移过程中可以消掉,于是我们将上式尽可能的将这一部分分解出来。
dpi,j=Si2+dpj−2SiSj−2SiL′+(Sj+L′)2dp_{i,j}=S_i^2+dp_j-2S_iS_j-2S_iL'+(S_j+L')^2dpi,j=Si2+dpj−2SiSj−2SiL′+(Sj+L′)2
接下来要比较哪个策略更优,于是我们假设存在j1<j2j_1<j_2j1<j2且dpi,j1≥dpi,j2dp_{i,j_1}\ge dp_{i,j_2}dpi,j1≥dpi,j2
Si2+dpj1−2SiSj1−2SiL′+(Sj1+L′)2≥Si2+dpj2−2SiSj2−2SiL′+(Sj2+L′)2S_i^2+dp_{j_1}-2S_iS_{j_1}-2S_iL'+(S_{j_1}+L')^2\ge S_i^2+dp_{j_2}-2S_iS_{j_2}-2S_iL'+(S_{j_2}+L')^2Si2+dpj1−2SiSj1−2SiL′+(Sj1+L′)2≥Si2+dpj2−2SiSj2−2SiL′+(Sj2+L′)2
dpj1−2SiSj1+(Sj1+L′)2≥dpj2−2SiSj2+(Sj2+L′)2dp_{j_1}-2S_iS_{j_1}+(S_{j_1}+L')^2\ge dp_{j_2}-2S_iS_{j_2}+(S_{j_2}+L')^2dpj1−2SiSj1+(Sj1+L′)2≥dpj2−2SiSj2+(Sj2+L′)2
根据前面的规定,在这个式子中,jjj是变量,iii是参数,但是我们没必要直接算出j1,j2j_1,j_2j1,j2和iii的关系,只需要简单的分离一下就行了。
2Si(Sj2−Sj1)≥dpj2−dpj1+(Sj2+L′)2−(Sj1+L′)22S_i(S_{j_2}-S_{j_1})\ge dp_{j_2}-dp_{j_1}+(S_{j_2}+L')^2-(S_{j_1}+L')^22Si(Sj2−Sj1)≥dpj2−dpj1+(Sj2+L′)2−(Sj1+L′)2
这里SSS是前缀和,这就意味着在输入均为正的情况下Sj2−Sj1≥0S_{j_2}-S_{j_1}\ge 0Sj2−Sj1≥0,于是可以进行下一步分离
2Si≥(dpj2+(Sj2+L′)2)−(dpj1+(Sj1+L′)2)Sj2−Sj12S_i\ge \frac{(dp_{j_2}+(S_{j_2}+L')^2)-(dp_{j_1}+(S_{j_1}+L')^2)}{S_{j_2}-S_{j_1}}2Si≥Sj2−Sj1(dpj2+(Sj2+L′)2)−(dpj1+(Sj1+L′)2)
这个两值之差的比很容易让人联想到斜率kkk(这就是这种优化叫做斜率优化的原因)。
处理斜率
我们设对于dpi,jdp_{i,j}dpi,j,在平面直角坐标系上有一个点Pj(Sj,dpj+(Sj+L’)2)P_{j}(S_j,dp_j+(S_j+L’)^2)Pj(Sj,dpj+(Sj+L’)2)。
根据上述结论,点Pj2P_{j_2}Pj2优于Pj1P_{j_1}Pj1当且仅当ΔyΔx≤2Si\frac{\Delta y}{\Delta x}\le2S_iΔxΔy≤2Si,否则Pj1P_{j_1}Pj1更优。
此时dpi,jdp_{i,j}dpi,j可以写作Yj−2Xj+Si−2SiL′Y_j-2X_j+S_i-2S_iL'Yj−2Xj+Si−2SiL′
接下来从坐标系上看多个点之间的关系,如图所示:
记图中的点A:Pj1 B:Pj2 C:Pj3A:P_{j_1} \space\space\space B:P_{j_2}\space\space\space C:P_{j_3}A:Pj1 B:Pj2 C:Pj3方便处理,不妨设k1>k2k_1> k_2k1>k2。
同时设2Si=k02S_i=k_02Si=k0,即刚刚计算得到的分界线。
对于k1k_1k1和k2k_2k2本身,可以以它们分别与k0k_0k0的关系为划分依据分类讨论。
(1)\mathbb{(1)}(1)当k1≤k0k_1\le k_0k1≤k0时,j2j_2j2优于j1j_1j1,反之当k1>k0k_1>k_0k1>k0时,j1j_1j1优于j2j_2j2。
(2)\mathbb{(2)}(2)当k2≤k0k_2\le k_0k2≤k0时,j3j_3j3优于j2j_2j2,反之当k1>k0k_1>k_0k1>k0时,j2j_2j2优于j3j_3j3。
接下来将k1,k2,k0k_1,k_2,k_0k1,k2,k0的关系放在一起讨论,因为前面k1>k2k_1> k_2k1>k2确定,所以有三种情况:
(1)\mathbb{(1)}(1)当k0<k2<k1k_0<k_2<k_1k0<k2<k1时,j1j_1j1优于j2j_2j2优于j3j_3j3,此时取j1j_1j1。
(2)\mathbb{(2)}(2)当k2≤k0<k1k_2\le k_0<k_1k2≤k0<k1时,j1j_1j1优于j2j_2j2,j3j_3j3优于j2j_2j2,此时取j1j_1j1或j3j_3j3。
(3)\mathbb{(3)}(3)当k2<k1≤k0k_2<k_1\le k_0k2<k1≤k0时,j3j_3j3优于j2j_2j2优于j1j_1j1,此时取j3j_3j3。
综上,容易发现无论如何都不会取j2j_2j2,于是我们可以直接忽略BBB点,如图所示:
经过一系列这样的删点操作,我们得到了一个图像。
容易发现,斜率是递增的,因为当一个点插入后不满足斜率递增就会被删除(即图中的LLL点)。
同时我们知道,当kj≤k0=2Sik_j\le k_0=2S_ikj≤k0=2Si时,最优点在jmaxj_{max}jmax取到;当k0<kjk_0<k_jk0<kj时,最优点在jminj_{min}jmin取到,于是我们需要找到第一个满足kj≥k0k_j\ge k_0kj≥k0的一段直线,其左端点恰好是最优解。又回到单调性问题,因为SiS_iSi单增,相邻斜率亦单增,所以可以用单调队列解决。
代码
注意到数据范围会超intintint,所以不开long longlong \ longlong long__ __ __。
另外kkk值可能是小数,不仅要开doubledoubledouble,还要$$
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N=5e4+10;
LL n,L;
long double s[N];
LL dp[N];
LL q[N],l,r;
long double k(int a,int b)
{
return ((dp[a]+(s[a]+L)*(s[a]+L))-(dp[b]+(s[b]+L)*(s[b]+L)))/(s[a]-s[b]);
}
int main()
{
cin>>n>>L;
L++;
for(int i=1;i<=n;i++)
{
cin>>s[i];
s[i]+=s[i-1]+1;
}
l=1,r=1;
for(int i=1;i<=n;i++)
{
while(l<r&&k(q[l],q[l+1])<2*s[i])
l++;
dp[i]=dp[q[l]]+(s[i]-s[q[l]]-L)*(s[i]-s[q[l]]-L);
while(l<r&&k(q[r],q[r-1])>=k(q[r-1],i))
r--;
q[++r]=i;
}
cout<<dp[n]<<endl;
return 0;
}