[题解]P2120 [ZJOI2007] 仓库建设

思路

我们定义 \(dp_i\) 表示运输前 \(i\) 个工厂,且在第 \(i\) 个位置建立仓库所消耗的最小代价。

那么,我们可以得出状态转移方程:

\[ dp_i = \min(dp_j + x_i \times (\sum_{k = j + 1}^{i}p_k) - \sum_{k = j + 1}^{i}(x_k \times p_k) + c_i) \]

然后用前缀和优化一下:

\[ dp_i = \min(dp_j + x_i \times (sp_i - sp_j) - (sf_i - sf_j) + c_i) \]

拆开括号得:

\[ dp_i = \min(dp_j - x_i \times sp_j + sf_j + x_i \times sp_i - sf_i + c_i) \]

如果 \(j_2\) 的代价优于 \(j_1\),那么当且仅当满足以下条件:

\[ dp_{j_1} - x_i \times sp_{j_1} + sf_{j_1} + x_i \times sp_i - sf_i + c_i > dp_{j_2} - x_i \times sp_{j_2} + sf_{j_2} + x_i \times sp_i - sf_i + c_i \]

化简得:

\[ dp_{j_1} - x_i \times sp_{j_1} + sf_{j_1} > dp_{j_2} - x_i \times sp_{j_2} + sf_{j_2} \]

\[ (dp_{j_1} + sf_{j_1}) - (dp_{j_2} + sf_{j_2}) > x_i \times (sp_{j_1} - sp_{j_2}) \]

\[ x_i < \frac{(dp_{j_1} + sf_{j_1}) - (dp_{j_2} + sf_{j_2})}{sp_{j_1} - sp_{j_2}} \]

不妨令:

  1. \(dp_{x} + sf_x\)\(Y(x)\)
  2. \(sp_x\)\(X(x)\)
  3. \(x_i\)\(K\)

那么有:

\[ K < \frac{Y(j_2) - Y(j_1)}{X(j_2) - X(j_1)} \]

然后因为 \(p_i\) 有可能为 \(0\),所以我们找答案时倒着找第一个 \(p_i\) 不为 \(0\) 的下标 \(t\),那么,答案就为 \(\min_{t \leq i\leq n}{dp_i}\)

Code

#include <bits/stdc++.h>  
#define int long long  
#define re register  
  
using namespace std;  
  
const int N = 1e6 + 10,inf = 1e18 + 10;  
int n,hh = 1,tt = 1;  
int x[N],p[N],c[N],sp[N],sf[N],dp[N],q[N];  
  
inline int read(){  
    int r = 0,w = 1;  
    char c = getchar();  
    while (c < '0' || c > '9'){  
        if (c == '-') w = -1;  
        c = getchar();  
    }  
    while (c >= '0' && c <= '9'){  
        r = (r << 3) + (r << 1) + (c ^ 48);  
        c = getchar();  
    }  
    return r * w;  
}  
  
inline int K(int i){  
    return x[i];  
}  
  
inline int X(int i){  
    return sp[i];  
}  
  
inline int Y(int i){  
    return dp[i] + sf[i];  
}  
  
inline double sl(int i,int j){  
    return 1.0 * (Y(j) - Y(i)) / (X(j) - X(i));  
}  
  
signed main(){  
    n = read();  
    for (re int i = 1;i <= n;i++){  
        x[i] = read();  
        p[i] = read();  
        c[i] = read();  
        sp[i] = sp[i - 1] + p[i];  
        sf[i] = sf[i - 1] + x[i] * p[i];  
    }  
    for (re int i = 1;i <= n;i++){  
        while (hh < tt && K(i) >= sl(q[hh],q[hh + 1])) hh++;  
        int j = q[hh];  
        dp[i] = dp[j] + x[i] * (sp[i] - sp[j]) - (sf[i] - sf[j]) + c[i];  
        while (hh < tt && sl(q[tt],q[tt - 1]) >= sl(i,q[tt - 1])) tt--;  
        q[++tt] = i;  
    }  
    int t = n;  
    while (!p[t] && t) t--;  
    if (!t) puts("0");  
    else{  
        int ans = inf;  
        for (re int i = t;i <= n;i++) ans = min(ans,dp[i]);  
        printf("%lld",ans);  
    }  
    return 0;  
}  
posted @ 2024-06-26 12:34  WBIKPS  阅读(33)  评论(0)    收藏  举报