仓库建设
luogu 2120
题目大意
有一个斜坡,上面有n个工厂(山顶是1,山脚是nnn,工厂都是漏填),上面有pip_ipi个货物,和工厂1的距离为x1x_1x1
现在有一场大雨,你可以在某些工厂处建立仓库(费用是cic_ici),没有建立仓库的工厂要把货物运到更低的仓库(及编号越大的仓库),运费是货物数∗*∗距离
现在问你全部货物运到仓库中最少需要多少钱
输入样例
3
0 5 10
5 3 100
9 6 10
输出样例
32
样例说明
在工厂 1 和工厂 3 建立仓库,建立费用为 10+10=2010+10=2010+10=20 ,运输费用为 (9−5)×3=12(9-5) \times 3 = 12(9−5)×3=12,总费用 32。
数据范围
对于 20%20\%20% 的数据,保证 n≤500n \leq 500n≤500。
对于 40%40\%40% 的数据,保证 n≤104n \leq 10^4n≤104 。
对于 100%100\%100% 的数据,保证 1≤n≤1061 \leq n \leq 10^61≤n≤106 ,0≤xi,pi,ci<2310 \leq x_i,~p_i,~c_i < 2^{31}0≤xi, pi, ci<231 。
对于任意的 1≤i<n1 \leq i < n1≤i<n,保证 xi<xi+1。x_i < x_{i + 1} 。xi<xi+1。
设答案为 ansansans,保证 ans+∑i=1npixi<263ans + \sum_{i = 1}^{n} p_ix_i < 2^{63}ans+∑i=1npixi<263 。
解题思路
我们设fif_ifi为在iii处建仓库,前iii个工厂的货物全部运到仓库的最小费用(这里把ppp改为sss,把xxx改为vvv)
我们就可以得出转移方程
fi=min{fj+ci+∑k=j+1i−1(vi−vk)sk}=min{fj+ci+∑k=j+1i(vi−vk)sk}=min{fj+ci+∑k=j+1ivisk−∑k=j+1ivksk}=min{fj+ci+(sumsi−sumsj)vi−(vsi−vsj)}\begin{aligned}f_i & = \min\{f_j+c_i+\sum_{k=j+1}^{i-1}(v_i-v_k)s_k\} \\ & = \min\{f_j+c_i+\sum_{k=j+1}^{i}(v_i-v_k)s_k\} \\ & =\min\{f_j+c_i+\sum_{k=j+1}^{i}v_is_k-\sum_{k=j+1}^{i}v_ks_k\} \\ & = \min\{f_j + c_i + (sums_i-sums_j)v_i - (vs_i - vs_j)\} \end{aligned}fi=min{fj+ci+k=j+1∑i−1(vi−vk)sk}=min{fj+ci+k=j+1∑i(vi−vk)sk}=min{fj+ci+k=j+1∑ivisk−k=j+1∑ivksk}=min{fj+ci+(sumsi−sumsj)vi−(vsi−vsj)}
注:
第二行加了iii这一项,但因为vi−vi=0v_i-v_i=0vi−vi=0所以结果不变
第四行vsi=∑j=1ivisivs_i=\sum_{j=1}^{i} v_is_ivsi=∑j=1ivisi
若现在有两个决策点a,ba,ba,b满足a>b,aa>b,aa>b,a优于bbb
则:
fa+ci+(sumsi−sumsa)vi−(vsi−vsa)<fb+ci+(sumsi−sumsb)vi−(vsi−vsb)f_a + c_i + (sums_i-sums_a)v_i - (vs_i - vs_a) < f_b + c_i + (sums_i-sums_b)v_i - (vs_i - vs_b)fa+ci+(sumsi−sumsa)vi−(vsi−vsa)<fb+ci+(sumsi−sumsb)vi−(vsi−vsb)
fa+ci+sumsivi−sumsavi−vsi+vsa<fb+ci+sumsivi−sumsbvi−vsi+vsbf_a + c_i + sums_iv_i-sums_av_i - vs_i + vs_a < f_b + c_i + sums_iv_i-sums_bv_i - vs_i + vs_bfa+ci+sumsivi−sumsavi−vsi+vsa<fb+ci+sumsivi−sumsbvi−vsi+vsb
fa−sumsavi+vsa<fb−sumsbvi+vsbf_a-sums_av_i + vs_a < f_b-sums_bv_i + vs_bfa−sumsavi+vsa<fb−sumsbvi+vsb
(fa+vsa)−(fb+vsb)<sumsavi−sumsbvi(f_a+vs_a)-(f_b+vs_b)< sums_av_i-sums_bv_i(fa+vsa)−(fb+vsb)<sumsavi−sumsbvi
(fa+vsa)−(fb+vsb)<sumsavi−sumsbvi(f_a+vs_a)-(f_b+vs_b)< sums_av_i-sums_bv_i(fa+vsa)−(fb+vsb)<sumsavi−sumsbvi
((fa+vsa)−(fb+vsb))/(sumsa−sumsb)<vi((f_a+vs_a)-(f_b+vs_b))/(sums_a-sums_b)<v_i((fa+vsa)−(fb+vsb))/(sumsa−sumsb)<vi
我们设
yi=fi+vsay_i=f_i+vs_ayi=fi+vsa
xi=sumsix_i=sums_ixi=sumsi
则
(ya−yb)/(xa−xb)<vi(y_a-y_b)/(x_a-x_b)<v_i(ya−yb)/(xa−xb)<vi
然后按照斜率优化模板套即可
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
ll n, l, r, v[1000050], s[1000050], c[1000050], d[1000050], y[1000050], f[1000050], vs[1000050];
int main()
{scanf("%d", &n);for (int i = 1; i <= n; ++i){scanf("%d%d%d", &v[i], &s[i], &c[i]);vs[i] = vs[i - 1] + v[i] * s[i];s[i] += s[i - 1];//s没用,就直接弄成前缀和}for (int i = 1; i <= n; ++i){while(r > l && (y[d[l + 1]] - y[d[l]]) < v[i] * (s[d[l + 1]] - s[d[l]])) l++;//模板f[i] = f[d[l]] + c[i] + (s[i] - s[d[l]]) * v[i] - (vs[i] - vs[d[l]]);y[i] = f[i] + vs[i];while(r > l && (y[i] - y[d[r]])*(s[d[r]] - s[d[r-1]]) < (y[d[r]] - y[d[r-1]])*(s[i] - s[d[r]])) r--;d[++r] = i;}printf("%d", f[n]);return 0;
}