CF2023B Skipping题解
观察这个操作是什么,首先显然的,对于 \(b_i<i\) 的操作,显然不会跳过,那么你剩下的操作就是通过若干次 \(b\) 的跳跃到达一个前缀,将前缀剩下的数全部选掉,于是动态规划,设 \(f_i\) 表示覆盖前 \(i\) 个最少代价,转移相当于对于 \([i+1,b_i]\) 用 \(f_i+a_i\) 取最小值,可以线段树,也可以使用 multiset 维护当前能更新的数,每次取最小值,最后再在每个转移的右端点删掉即可。
代码:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 400400, inf = 0x3f3f3f3f3f3f3f3f;;
int T, n, ans;
int a[N], b[N], f[N];
multiset<int> S, del[N];
signed main() {
scanf("%lld", &T);
while (T--) {
scanf("%lld", &n), ans = -inf;
for (int i = 1; i <= n; ++i) scanf("%lld", a + i);
for (int i = 1; i <= n; ++i) scanf("%lld", b + i), f[i] = inf, del[i].clear();
S.clear(), f[1] = 0, del[1].insert(0), S.insert(0);
for (int i = 1; i <= n; ++i) {
if (!S.empty()) f[i] = min(f[i], *S.begin());
if (b[i] > i) S.insert(f[i] + a[i]), del[b[i]].insert(f[i] + a[i]);
for (int x : del[i]) S.erase(S.find(x));
}
for (int i = 1, sm = 0; i <= n; ++i)
sm += a[i], ans = max(ans, sm - f[i]);
printf("%lld\n", ans);
}
return 0;
}

浙公网安备 33010602011771号