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

Gypsophila

2018-08-15 22:17:46

Solution

### Solution 斜率优化dp。设 $dp_i$ 为在 $i$ 位置建设了仓库的最小代价。 则有 $$dp_i=\min\limits_{0 \leq j<i} \{dp_j+x_i\times\sum\limits_{l=j+1}^{i}(p_l) -\sum\limits_{l=j+1}^{i}(x_l \times p_l)\}+c_i$$ 用前缀和优化,令 $sump_i=\sum\limits_{j=1}^{i}p_i,sum_i=\sum\limits_{j=1}^{i}x_i\times p_i$,则原式转为 $$dp_i=\min\limits_{0 \leq j < i}\{dp_j+x_i\times(sump_i-sump_j)-(sum_i-sum_j)\}+c_i$$ 若决策 $j$ 优于决策 $k$ ,则 $$dp_j+x_i\times(sump_i-sump_j)-(sum_i-sum_j)<dp_k+x_i\times(sump_i-sump_k)-(sum_i-sum_k)$$ 化简得 $$(dp_j+sum_j)-(dp_k+sum_k)<x_i \times (sump_j-sump_k)$$ 即 $$\frac{(dp_j+sum_j)-(dp_k+sum_k)}{sump_j-sump_k}<x_i$$ 令 $Y(x)=dp_x+sum_x,X(x)=sump_x$,则 $$\frac{Y(j)-Y(k)}{X(j)-X(k)}<x_i$$ 上斜率优化即可 ### Code ```cpp #include <iostream> #include <cstdlib> #include <cstdio> #define int long long #define rep(i, l, r) for(register int i = l; i <= r; i++) #define per(i, r, l) for(register int i = r; i >= l; i--) using namespace std; const int N = 10010000; int n, x[N], p[N], c[N], dp[N]; int sum[N], sump[N], h, t, Q[N]; inline int Y(int i) { return dp[i] + sum[i]; } inline int X(int i) { return sump[i]; } inline double slope(int j, int k) { return 1.0 * (Y(j) - Y(k)) / (X(j) - X(k)); } signed main() { scanf("%d", &n); rep(i, 1, n) { scanf("%d%d%d", &x[i], &p[i], &c[i]); sump[i] = sump[i - 1] + p[i]; sum[i] = sum[i - 1] + x[i] * p[i]; } rep(i, 1, n) { while(h < t && slope(Q[h + 1], Q[h]) < x[i]) h++; int j = Q[h]; dp[i] = dp[j] + x[i] * (sump[i] - sump[j]) - sum[i] + sum[j] + c[i]; while(h < t && slope(Q[t], Q[t - 1]) > slope(i, Q[t])) t--; Q[++t] = i; } printf("%lld\n", dp[n]); return 0; } ```