仓库建设[ZJOI2007]
题目描述
https://www.luogu.com.cn/problem/P2120
题解
设 \(S_i=\sum\limits_{j=1}^{i} p_j\) ,\(T_i=\sum\limits_{j=1}^{i} p_j*a_j\)
\(S_i\) 即为前 \(i\) 个工厂的物品总数,\(T_i\) 为前 \(i\) 个工厂都把所有物品运到工厂 \(1\) 的总花费(虽然不能运到仓库 \(1\) )
设 \(f_i\) 表示把前 \(i\) 个工厂的物品都运到仓库里且在 \(i\) 工厂处建立仓库的最小花费
则有 \(f_i=\min\limits_{1\le j < i} f_j + a_i * (S_i-S_j) - (T_i-T_j) + c_i\)
变化一下式子就是 \((f_j+T_j)=a_i*S_j+(F_i-a_i*S_i-c_i+T_i)\)
使用斜率优化即可
代码
#include <bits/stdc++.h>
#define N 1000005
using namespace std;
typedef long long ll;
template <typename T>
inline void read(T &num) {
T x = 0, f = 1; char ch = getchar();
for (; ch > '9' || ch < '0'; ch = getchar()) if (ch == '-') f = -1;
for (; ch <= '9' && ch >= '0'; ch = getchar()) x = (x << 3) + (x << 1) + (ch ^ '0');
num = x * f;
}
int n, head, tail, q[N];
ll a[N], p[N], c[N], dp[N], sum[N], sum2[N], K[N];
inline ll getx(int id) {
return sum[id];
}
inline ll gety(int id) {
return dp[id] + sum2[id];
}
inline double slope(int x, int y) {
return getx(x) == getx(y) ? 1e15 : 1.0 * (gety(x) - gety(y)) / (getx(x) - getx(y));
}
int main() {
read(n);
for (int i = 1; i <= n; i++) {
read(a[i]); read(p[i]); read(c[i]);
sum[i] = sum[i-1] + p[i];
sum2[i] = sum2[i-1] + a[i] * p[i];
K[i] = a[i];
}
q[head=tail=1] = 0;
for (int i = 1; i <= n; i++) {
while (head < tail && slope(q[head], q[head+1]) < K[i]) head++;
int j = q[head];
dp[i] = dp[j] + a[i] * (sum[i] - sum[j]) - (sum2[i] - sum2[j]) + c[i];
while (head < tail && slope(q[tail-1], q[tail]) > slope(q[tail], i)) tail--;
q[++tail] = i;
}
printf("%lld\n", dp[n]);
return 0;
}