题解:AT_abc209_f [ABC209F] Deforestation
可逐步展开每个 block。
::::info[block1.最小总代价如何刻画]
hint1
不要研究砍树顺序。考虑对于高度
代价只可能从左边,右边,自己出来。那么可以将总代价写成:
那么考虑对于两颗相邻的树
answer1
显然是先砍大的,交换证明即可。
所以得出结论:
-
h_i < h_{i-1}$,对应的 $p_i > p_{i-1} -
h_i > h_{i-1}$,对应的 $p_i < p_{i-1} -
::::
::::info[block2.转化成插入/相对顺序模型]
hint2
假设前
answer2
-
-
-
::::
::::info[block3.状态定义]
hint3
都推到了第二部分了,还不能写转移方程吗?动态规划状态定义?转移方程?
answer3
定义
状态转移方程:
此时已经可以写出来
::::info[block4.前缀和优化]
hint4
已经做出来了但是时间复杂度不行,怎么办?
answer4
注意到转移中只有加法,可以使用前缀和优化动态规划,即定义
::::
代码
#include <bits/stdc++.h>
using namespace std;
namespace hjyowl {
const long long N = 5010, mod = 1e9 + 7;
long long s[N][N], f[N][N];
long long a[N];
long long main() {
long long n;
cin >> n;
for (long long i = 1; i <= n; i++) {
cin >> a[i];
}
f[1][1] = 1;
for (long long i = 1; i <= n; i++) {
s[1][i] = 1;
}
for (long long i = 2; i <= n; i++) {
for (long long j = 1; j <= i; j++) {
if (a[i] > a[i - 1]) {
f[i][j] = (s[i - 1][i - 1] - s[i - 1][j - 1] + mod) % mod;
} else if (a[i] < a[i - 1]) {
f[i][j] = s[i - 1][j - 1] % mod;
} else {
f[i][j] = s[i - 1][i - 1] % mod;
}
s[i][j] = (s[i][j - 1] + f[i][j]) % mod;
}
}
long long res = 0;
for (long long i = 1; i <= n; i++) {
res = (res + f[n][i]) % mod;
}
cout << res;
return 0;
}
} // namespace hjyowl
int main() { return hjyowl::main(); }