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;
}
View Code

 

posted @ 2020-01-27 18:01  Mrzdtz220  阅读(121)  评论(0编辑  收藏  举报