仓库建设

 

Problem

Source: BZOJ 1096, ZJOI 2007

Description

$L$ 公司有 $N$ 个工厂,由高到底分布在一座山上。如图所示,工厂 $1$ 在山顶,工厂 $N$ 在山脚:

img

由于这座山处于高原内陆地区(干燥少雨),$L$ 公司一般把产品直接堆放在露天,以节省费用。突然有一天,$L$ 公司的总裁 $L$ 先生接到气象部门的电话,被告知三天之后将有一场暴雨,于是 $L$ 先生决定紧急在某些工厂建立一些仓库以免产品被淋坏。

由于地形的不同,在不同工厂建立仓库的费用可能是不同的。第 $i$ 个工厂目前已有成品 $P_i$ 件,在第 $i$ 个工厂位置建立仓库的费用是 $C_i$。对于没有建立仓库的工厂,其产品应被运往其他的仓库进行储藏,而由于 $L$ 公司产品的对外销售处设置在山脚的工厂 $N​$,故产品只能往山下运(即只能运往编号更大的工厂的仓库),当然运送产品也是需要费用的,假设一件产品运送 1​ 个单位距离的费用是 ​1​。假设建立的仓库容量都都是足够大的,可以容下所有的产品。

你将得到以下数据:

  • 工厂 $i$ 距离工厂 $1$ 的距离 $X_i$(其中 $X_1=0$)

  • 工厂 $i$ 目前已有成品数量 $P_i$

  • 在工厂 $i$ 建立仓库的费用 $C_i$

请你帮助 $L$ 公司寻找一个仓库建设的方案,使得总的费用(建造费用 + 运输费用)最小。

 

数据规模:

  • 对于 $20\%$ 的数据,$N \leq 500$

  • 对于 $40\%$ 的数据,$N \leq 10000$

  • 对于 $100\%$ 的数据,$N \leq 1000000$

所有的 $X_i, P_i, C_i$ 均在 32 位带符号整数以内,保证中间计算结果不超过 64​ 位带符号整数。

Input

第一行包含一个整数 $N$,表示工厂的个数。接下来 $N$ 行每行包含两个整数 $X_i, P_i, C_i$,意义如题中所述。

Output

仅包含一个整数,为可以找到最优方案的费用。

Sample Input

3
0 5 10
5 3 100
9 6 10

Sample Output

32

Output Details

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

如果仅在工厂 $3$ 建立仓库,建立费用为 $10$,运输费用为 $(9-0) \times 5+(9-5) \times 3=57$,总费用为 67​,不如前者优。

 

Solution

$f(i)$:在 $i$ 处建立仓库,把 $1$ 到 $i$ 的成品运送到满足题意的仓库中的最少费用

$\text{Sum}(i)$:从 $1$ 到 $i$ 的成品数量之和

$\text{Sumd}(i)$:将 $1$ 到 $i$ 的成品全部运送到 $i$ 的花费

状态转移方程:

\[\begin{cases} f(i) = f(j) + \text{Sumd}(i) - \text{Sumd}(j) - \text{Sum}(j) * (\text{Dis}(i) - \text{Dis}(j)) + \text{Cost}(i) \quad (0 \leq j \leq i-1) \\ \text{Sumd}(i) = \text{Sumd}(i-1) + \text{Sum}(i-1) * (\text{Dis}(i) - \text{Dis}(i-1)) \end{cases}\]

设 $j<k$ 且决策 $k$ 比 $j​$ 优,即:

\[f(k) + \text{Sumd}(i) - \text{Sumd}(k) - \text{Sum}(k) * (\text{Dis}(i) - \text{Dis}(k)) + \text{Cost}(i)\] \[< f(j) + \text{Sumd}(i) - \text{Sumd}(j) - \text{Sum}(j) * (\text{Dis}(i) - \text{Dis}(j)) + \text{Cost}(i)\]

化简后:

\[\frac{\Big ( f(j) - \text{Sumd}(j) + \text{Sum}(j) * \text{Dis}(j) \Big ) - \Big ( f(k) - \text{Sumd}(k) + \text{Sum}(k) * \text{Dis}(k) \Big )}{\text{Sum}(j) - \text{Sum}(k)} < \text{Dis}(i)\]

然后用斜率优化。

Code

Github (C++)

#include <iostream>
#include <cstdio>
#include <iomanip>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;

typedef long long ll;

const int M = 1000005;
ll Dis[M], Cost[M], Sum[M], Sumd[M], f[M], q[M];
int n;

void Init() {
    scanf("%d",&n);
    for(int i = 1; i <= n; i++) {
        scanf("%lld%lld%lld",&Dis[i], &Sum[i], &Cost[i]);
        Sum[i] += Sum[i-1];
        Sumd[i] = Sumd[i-1] + Sum[i-1] * (Dis[i] - Dis[i-1]);
    }
}

double Slope(int i,int j) {
    return double((f[i] - Sumd[i] + Sum[i] * Dis[i]) - (f[j] - Sumd[j] + Sum[j] * Dis[j])) / (Sum[i] - Sum[j]);
}

void Dp() {
    int L = 1, R = 1;

    for(int i = 1; i <= n; i++) {
        while(L < R && Slope(q[L],q[L+1]) < Dis[i]) L++;
        int j = q[L];
        f[i] = f[j] + Sumd[i] - Sumd[j] - Sum[j] * (Dis[i] - Dis[j]) + Cost[i];
        while(L < R && Slope(q[R-1], q[R]) > Slope(q[R], i)) R--;
        q[++R] = i;
    }

    printf("%lld\n", f[n]);
}

int main() {
    Init();
    Dp();
    return 0;
}