题解 BZOJ 3156 防御准备

题目大意:

给定一个 \(n\)(\(1 <= n <= 10^6\)) 以及一个长度为 \(n\)正整数 数列 \(a\) ,其中 \(1 <= a_i <= 10^9\)

假设现在你在 \(i\) 点,你只有两个选择 :

  • 在当前点花费 \(a_i\) 点费用修建一个 "保卫塔"。
  • 花费 \(dis\) 点费用修建一个 "木偶" , \(dis\) 表示的就是 \(i\) 点右侧的第一个修建了 "保卫塔"的点到 \(i\) 的距离。

特别的是,对于第 \(n\) 个点必须要选择修建 "保卫塔"。现在问你最小的费用是多少。

\(example:\)
\(input:\)

10
2 3 1 5 4 5 6 3 1 2

\(output:\)

18

解题思路:

状态设置:

\(dp[i]\) 表示处理完 \(1 ~ i\) 的所有点的最小花费并且 \(i\) 必须放置 "守卫塔"。
最后的答案自然是 \(dp[n]\)

状态转移:

$dp[i] $ = \(min(dp[j] + (j - i + 1) * (j - i) / 2 + A[i]) (1 <= j < i)\)

随便搞一搞弄个斜率优化。

处理 \(i\) 点的时候,我们假设 \(j\) < \(k\) ,并且选择 \(j\) 进行转移更优:
\(dp[j][1] + (j - i + 1) * (j - i) / 2 + A[i] < dp[k][1] + (k - i + 1) * (k - i) / 2 + A[i]\)

\(dp[j][1] - dp[k][1] < (k - i)^2 + (j - i)^2 + k - j\)

\(\frac{2 * (dp[j][1] - dp[k][1]) - k * (k + 1) + j * (j + 1)}{2 * (j - k)} < i\)

则这时候选择 \(j\) 进行转移更优秀,斜率优化即可。

Code

#include <bits/stdc++.h>
using namespace std;
#define int long long
inline int read() {
    int x = 0 , flag = 1;
    char ch = getchar();
    for( ; ch > '9' || ch < '0' ; ch = getchar()) if(ch == '-') flag = -1;
    for( ; ch >= '0' && ch <= '9' ; ch = getchar()) x = (x << 3) + (x << 1) + ch - '0';
    return x * flag;
}
typedef double ld;
const int MAXN = 1e6 + 50;
int n,A[MAXN],q[MAXN];
int dp[MAXN];
double calc(int j,int k) {
    return (ld)(dp[j] - dp[k] + (j * (j + 1) - k * (k + 1)) / 2) / (ld)(j - k);
}

signed main() {
    n = read();
    for(int i = 1 ; i <= n ; i ++) A[i] = read();
    int l = 1 , r = 1; q[1] = 0;
    for(int i = 1 ; i <= n ; i ++) {
        while(l < r && calc(q[l], q[l + 1]) < (ld)i) l ++;
        dp[i] = dp[q[l]] + (i - q[l] - 1) * (i - q[l]) / 2 + A[i];
        while(l <= r && calc(q[r - 1], q[r]) >= calc(q[r], i)) r --;
        q[++r] = i;
    }
    cout << dp[n];
    return 0;
}
posted @ 2021-02-20 14:09  MYCui  阅读(30)  评论(0编辑  收藏  举报