BZOJ 1096. [ZJOI2007]仓库建设
$dp_i$ 表示在 $i$ 处建一个仓库之后的最小花费。
转移方程为
$dp_i = \min \{dp_j + \sum \limits_{k=j+1}^{i-1}p_k\times(x_i-x_k)+c_i\}=\min \{dp_j + x_i\sum \limits_{k=j+1}^{i-1}p_k-\sum\limits_{k=j+1}^{i-1} p_kx_k+c_i\}$
设 $g_i=-\sum \limits_{j=1}^i p_jx_j$,$p_i$ 变成前缀和。
转移方程变成 $dp_i=\min\{dp_j+x_i(p_{i-1}-p_{j})+g_{i-1}-g_j+c_i\}$,就可以斜率优化了,斜率是 $p_{i-1}$ 保证递增,那么就有决策单调性,用双端队列即可。
#include <bits/stdc++.h> #define ll long long using namespace std; namespace IO { const int MAXSIZE = 1 << 20; char buf[MAXSIZE], *p1, *p2; #define gc() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, MAXSIZE, stdin), p1 == p2) ? EOF : *p1++) template<typename T> inline void read(T &x) { x = 0; T f = 1; char ch = gc(); while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = gc(); } while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = gc(); } x *= f; } } using namespace IO; const int N = 1e6 + 7; ll x[N], p[N], c[N], g[N], dp[N]; int que[N]; inline ll X(int i) { return p[i]; } inline ll Y(int i) { return dp[i] - g[i]; } inline double K(int i, int j) { return (double)(Y(i) - Y(j)) / (double)(X(i) - X(j)); } int main() { //freopen("in.txt", "r", stdin); int n; read(n); for (int i = 1; i <= n; i++) { read(x[i]), read(p[i]), read(c[i]); g[i] = g[i - 1] - p[i] * x[i]; p[i] += p[i - 1]; } int l = 1, r = 1; for (int i = 1; i <= n; i++) { while (l < r && K(que[l], que[l + 1]) <= x[i]) l++; int j = que[l]; dp[i] = dp[j] + x[i] * (p[i - 1] - p[j]) + g[i - 1] - g[j] + c[i]; while (l < r && K(que[r], que[r - 1]) >= K(que[r], i)) r--; que[++r] = i; } printf("%lld\n", dp[n]); return 0; }