题解 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;
}
By MYCui