确实需要加强优化 DP 方面的练习。
$O(n^2)$ 的 DP 有手就行。
至于优化,考虑到每次转移中有用的 $h_j \in [0,h_i)$,所以可以建立关于高度的值域线段树,存储 DP 值。
那转移相当于区间求 $\max$,单点修 $\max$。
套板子就完了。
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 15;
const long long INF = 1e18;
int n, m, h[N], v[N];
struct Tree {
int l, r;
long long Max;
} tr[N << 2];
void pushup(int u) {
tr[u].Max = max(tr[u << 1].Max, tr[u << 1 | 1].Max);
}
void build(int u, int l, int r) {
tr[u].l = l, tr[u].r = r;
if (l == r) {
if (l == 0) tr[u].Max = 0;
else tr[u].Max = -INF;
return;
}
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
pushup(u);
}
void change(int u, int x, long long d) {
if (tr[u].l == tr[u].r) {
tr[u].Max = max(tr[u].Max, d);
return;
}
int mid = tr[u].l + tr[u].r >> 1;
if (x <= mid) change(u << 1, x, d);
else change(u << 1 | 1, x, d);
pushup(u);
}
long long query(int u, int l, int r) {
if (tr[u].l >= l && tr[u].r <= r) return tr[u].Max;
int mid = tr[u].l + tr[u].r >> 1;
long long res = -INF;
if (l <= mid) res = max(res, query(u << 1, l, r));
if (r > mid) res = max(res, query(u << 1 | 1, l, r));
return res;
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &h[i]), m = max(m, h[i]);
for (int i = 1; i <= n; i++) scanf("%d", &v[i]);
build(1, 0, m);
for (int i = 1; i <= n; i++) {
long long Max = query(1, 0, h[i] - 1);
change(1, h[i], Max + v[i]);
}
printf("%lld\n", tr[1].Max);
return 0;
}