Problem 8 dp

$des$

$sol$

记 $f_i$ 表示考虑前 $i$ 个建筑, 并且第 $i$ 个建筑的高度不变的答案, 每次
转移时枚举上一个不变的建筑编号, 中间的一段一定变成相同的高度, 并且
高度小于等于两端的高度.
假设从 $f_j$ 转移且中间高度为 $t$, 则:
$$f_i = \sum_{k = j + 1} ^ {i - 1} (t - h_k) ^ 2 + c(h_j + h_i - 2t)$$
这样中间的高度可以 $O(1)$ 求二次函数的对称轴确定. 考虑优化转移,
因为中间高度要小于两端, 所以最多只有一个 $h_j > h_i$ 的 $j$ 能够转移. 可以
维护关于高度的单调栈, 这样有效的转移次数就是 O(n) 的.

$code$

#include <bits/stdc++.h>

using std::pair;
using std::vector;
using std::string;

typedef long long ll;
typedef pair<int, int> pii;

#define fst first
#define snd second
#define pb(a) push_back(a)
#define mp(a, b) std::make_pair(a, b)
#define debug(...) fprintf(stderr, __VA_ARGS__)

template <typename T> bool chkmax(T& a, T b) { return a < b ? a = b, 1 : 0; }
template <typename T> bool chkmin(T& a, T b) { return a > b ? a = b, 1 : 0; }

template <typename T> T read(T& x) {
    int f = 1; x = 0;
    char ch = getchar();
    for(;!isdigit(ch); ch = getchar()) if(ch == '-') f = -1;
    for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
    return x *= f;
}

const int N = 1000000;

int n, C;
int h[N + 5];
ll s[2][N + 5], dp[N + 5];

ll solve(int x, int y, int mx) {
    ll a = y - x - 1;
    ll b = -2 * (s[0][y-1] - s[0][x]) - (x != 0) * C - (y != n+1) * C;
    ll c = s[1][y-1] - s[1][x] + 1ll * (x != 0) * h[x] * C + 1ll * (y != n+1) * h[y] * C;

    ll t;
    t = (ll) ((- b / 2 / a) + 0.5);

    chkmax<ll>(t, mx);
    if(x != 0) chkmin(t, (ll) h[x]);
    if(y <= n) chkmin(t, (ll) h[y]);

    return a * t * t + b * t + c;
}

int main() {

    read(n), read(C);
    for(int i = 1; i <= n; ++i) {
        read(h[i]);
        s[0][i] = s[0][i-1] + h[i];
        s[1][i] = s[1][i-1] + 1ll * h[i] * h[i];
    }

    static int stk[N + 5], top;

    h[0] = h[n + 1] = (1 << 30);
    stk[top ++] = 0;

    for(int i = 1; i <= n+1; ++i) {
        dp[i] = dp[i-1] + ((i == 1 || i == n+1) ? 0 : 1ll * C * std::abs(h[i] - h[i-1]));
        while(top > 0 && h[stk[top-1]] <= h[i]) {
            if(top > 1) 
                chkmin(dp[i], dp[stk[top-2]] + solve(stk[top-2], i, h[stk[top-1]]));
            -- top;
        }
        stk[top ++] = i;
    }
    printf("%lld\n", dp[n + 1]);

    return 0;
}

 

posted @ 2018-10-13 17:10  xayata  阅读(229)  评论(2编辑  收藏  举报