【斜率优化】仓库建设(luogu 2120)

仓库建设

luogu 2120

题目大意

有一个斜坡,上面有n个工厂(山顶是1,山脚是 n n n,工厂都是漏填),上面有 p i p_i pi个货物,和工厂1的距离为 x 1 x_1 x1
现在有一场大雨,你可以在某些工厂处建立仓库(费用是 c i c_i ci),没有建立仓库的工厂要把货物运到更低的仓库(及编号越大的仓库),运费是货物数 ∗ * 距离
现在问你全部货物运到仓库中最少需要多少钱

输入样例

3
0 5 10
5 3 100
9 6 10

输出样例

32

样例说明

在工厂 1 和工厂 3 建立仓库,建立费用为 10 + 10 = 20 10+10=20 10+10=20 ,运输费用为 ( 9 − 5 ) × 3 = 12 (9-5) \times 3 = 12 (95)×3=12,总费用 32。

数据范围

对于 20 % 20\% 20% 的数据,保证 n ≤ 500 n \leq 500 n500
对于 40 % 40\% 40% 的数据,保证 n ≤ 1 0 4 n \leq 10^4 n104
对于 100 % 100\% 100% 的数据,保证 1 ≤ n ≤ 1 0 6 1 \leq n \leq 10^6 1n106 0 ≤ x i ,   p i ,   c i < 2 31 0 \leq x_i,~p_i,~c_i < 2^{31} 0xi, pi, ci<231
对于任意的 1 ≤ i < n 1 \leq i < n 1i<n,保证 x i < x i + 1 。 x_i < x_{i + 1} 。 xi<xi+1
设答案为 a n s ans ans,保证 a n s + ∑ i = 1 n p i x i < 2 63 ans + \sum_{i = 1}^{n} p_ix_i < 2^{63} ans+i=1npixi<263

解题思路

我们设 f i f_i fi为在 i i i处建仓库,前 i i i个工厂的货物全部运到仓库的最小费用(这里把 p p p改为 s s s,把 x x x改为 v v v
我们就可以得出转移方程
f i = min ⁡ { f j + c i + ∑ k = j + 1 i − 1 ( v i − v k ) s k } = min ⁡ { f j + c i + ∑ k = j + 1 i ( v i − v k ) s k } = min ⁡ { f j + c i + ∑ k = j + 1 i v i s k − ∑ k = j + 1 i v k s k } = min ⁡ { f j + c i + ( s u m s i − s u m s j ) v i − ( v s i − v s j ) } \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+1i1(vivk)sk}=min{fj+ci+k=j+1i(vivk)sk}=min{fj+ci+k=j+1iviskk=j+1ivksk}=min{fj+ci+(sumsisumsj)vi(vsivsj)}
注:
第二行加了 i i i这一项,但因为 v i − v i = 0 v_i-v_i=0 vivi=0所以结果不变
第四行 v s i = ∑ j = 1 i v i s i vs_i=\sum_{j=1}^{i} v_is_i vsi=j=1ivisi
若现在有两个决策点 a , b a,b a,b满足 a > b , a a>b,a a>b,a优于 b b b
则:
f a + c i + ( s u m s i − s u m s a ) v i − ( v s i − v s a ) < f b + c i + ( s u m s i − s u m s b ) v i − ( v s i − v s b ) 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+(sumsisumsa)vi(vsivsa)<fb+ci+(sumsisumsb)vi(vsivsb)
f a + c i + s u m s i v i − s u m s a v i − v s i + v s a < f b + c i + s u m s i v i − s u m s b v i − v s i + v s b f_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_b fa+ci+sumsivisumsavivsi+vsa<fb+ci+sumsivisumsbvivsi+vsb
f a − s u m s a v i + v s a < f b − s u m s b v i + v s b f_a-sums_av_i + vs_a < f_b-sums_bv_i + vs_b fasumsavi+vsa<fbsumsbvi+vsb
( f a + v s a ) − ( f b + v s b ) < s u m s a v i − s u m s b v i (f_a+vs_a)-(f_b+vs_b)< sums_av_i-sums_bv_i (fa+vsa)(fb+vsb)<sumsavisumsbvi
( f a + v s a ) − ( f b + v s b ) < s u m s a v i − s u m s b v i (f_a+vs_a)-(f_b+vs_b)< sums_av_i-sums_bv_i (fa+vsa)(fb+vsb)<sumsavisumsbvi
( ( f a + v s a ) − ( f b + v s b ) ) / ( s u m s a − s u m s b ) < v i ((f_a+vs_a)-(f_b+vs_b))/(sums_a-sums_b)((fa+vsa)(fb+vsb))/(sumsasumsb)<vi
我们设
x i = f i + v s a x_i=f_i+vs_a xi=fi+vsa
y i = s u m s i y_i=sums_i yi=sumsi

( x a − x b ) / ( y a − y b ) < v i (x_a-x_b)/(y_a-y_b)(xaxb)/(yayb)<vi
然后按照斜率优化模板套即可

代码

#include
#include
#include
#include
#define ll long long
using namespace std;
ll n, l, r, v[1000050], s[1000050], c[1000050], d[1000050], x[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 && (x[d[l + 1]] - x[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]]);
		x[i] = f[i] + vs[i];
		while(r > l && (x[i] - x[d[r]])*(s[d[r]] - s[d[r-1]]) < (x[d[r]] - x[d[r-1]])*(s[i] - s[d[r]])) r--;
		d[++r] = i;
	}
	printf("%d", f[n]);
	return 0;
}

你可能感兴趣的:(DP,#,斜率优化)