P3195 玩具装箱 题解 & 斜率优化入门
P3195 玩具装箱
简要题意:
有 \(n\) 个价值分别为 \(c_i\) 的玩具并给定常数 \(L\),要求将这 \(n\) 个玩具分成若干段,对于每一段区间 \([l,r]\),定义其代价为 \((r-l+\sum c_i-L)^2\),求分段的最小代价。
\(n\leq 5\times 10^4\)
分析:
定义 \(f_i\) 表示前 \(i\) 个物品分若干段的最小代价。
状态转移方程:
其中 \(sum_i\) 表示 \(c_i\) 的前缀和。
暴力转移,时间复杂度 \(O(n^2)\)。
发现转移方程中存在 \(i,j\) 的交叉项,考虑斜率优化。
简化一下方程:令 \(s_i=sum_i+i\),\(L'=L+1\),则状态转移方程:
对这个式子进行变形得到:
再把这个式子转化为 \(b=y-kx\) 的形式,具体地,设:
此时状态转移方程为:
既然要最小化 \(f_i\),而 \((s_i-L')^2\) 又是常数,问题就转化成了最小化 \(b_i\)
我们将 \((x_j,y_j)\) 看作平面上的点,\(k_i\) 表示直线的斜率,\(b_i\) 就表示一条过 \((x_j,y_j)\) 的、斜率为 \(k_i\) 的直线的截距,问题转化为选择合适的 \(j\),最小化这个截距。
因为要最小化这个截距,画一画图不难看出,可能进行转移的点集一定在下凸壳上,因此我们状态转移时只需考虑凸壳上的点,又发现本题 \(k_i\) 严格单调递增,因此我们可以利用单调队列维护凸壳。
具体地,每当遍历到一个点时,我们将先入队的斜率小的弹出,然后进行转移,转移后将后入队的不满足斜率单调性的弹出,最后把这个点加入队列,从而维护一个凸壳。
对于维护凸壳的操作,所有点最多只会进出队列一次,故均摊 \(O(1)\),总时间复杂度:\(O(n)\)