专栏文章
CF2167G 题解
CF2167G题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @minfvd5s
- 此快照首次捕获于
- 2025/12/02 01:44 3 个月前
- 此快照最后确认于
- 2025/12/02 01:44 3 个月前
传送门:洛谷 CF2167G Mukhammadali and the Smooth Array | Codeforces G. Mukhammadali and the Smooth Array
更佳的阅读体验:CF2167G 题解
在阅读本题解之前,请确保你已经对最长不降子序列(Longest Non-decrease Subsequence)模型有所了解。
简要题意:给你一个序列 ,你可以进行任意次操作,花费 的代价将 替换为任意整数,求将 变得单调不降的最小代价。
考虑这样一个事实:如果不作修改的点本身是单调不降的,那么就一定有修改方案使得整个序列单调不降。
我们希望总代价尽可能小,也就是保留的点的代价之和尽可能大,因此我们实际上想找的是一个单调不降的子序列,使得这个子序列的权值之和最大。此时,我们发现这实际上是一个带权最长不降子序列问题,直接套板子即可。
,那么 的暴力 DP 就足以通过,不需要任何优化。
CPP#include <iostream>
#include <algorithm>
using namespace std;
using ll = long long;
const int N = 8010;
int t, n, a[N];
ll c[N], f[N], sum;
int main() {
cin.tie(nullptr);
ios::sync_with_stdio(false);
for (cin >> t; t; --t) {
sum = 0;
cin >> n;
for (int i = 1; i <= n; ++i) cin >> a[i];
for (int i = 1; i <= n; ++i) cin >> c[i], sum += c[i];
for (int i = 1; i <= n; ++i) {
f[i] = c[i];
for (int j = 1; j < i; ++j)
if (a[j] <= a[i]) f[i] = max(f[i], f[j] + c[i]);
} cout << sum - *max_element(f + 1, f + n + 1) << '\n';
} return 0;
}
到这里,你已经可以通过本题。但如果你还有兴趣了解进一步的优化,欢迎继续阅读。
的复杂度只能勉强通过 的数据,那么如果 呢?我们很自然地想到了非带权的最长不降子序列问题的单调栈优化。
那么,这样的优化能否用在带权的问题上呢?
答案是否定的。
回顾朴素问题的单调栈优化。我们维护了一个尾部数组 , 表示长度为 的不降子序列中,末尾元素的最小值。当我们枚举到一个新的数 时,我们通过二分找到 应该放入的位置,并更新对应的元素。
这种贪心的正确性在于,对于同样长度的子序列,如果末尾的数更小,那么一定不会比末尾的数更大的方案更劣,因为末尾更小的子序列更有可能在接下来的枚举中被延长。
然而在本题中,我们要求的并不是最大的长度,而是最大的保留元素的权值和。这样,“末尾更小一定更优”的思想就不一定正确了。
观察上面那份代码的 DP 过程,我们实际上可以写出下面的转移方程:
观察这个“前缀最大值”的形式,我们立刻想到可以使用一个类似权值树状数组的数据结构,来维护值域上的 的最大值。
由于 的数据范围比较大,我们还需要对 做一次离散化。
时间复杂度 。
CPP#include <iostream>
#include <algorithm>
using namespace std;
using ll = long long;
const int N = 8010;
int t, n, a[N], d[N];
ll c[N], sum, ans;
struct BIT {
ll maxn[N];
void clear() {
for (int i = 1; i <= n; ++i) maxn[i] = 0;
} void update(int k, ll x) {
for (; k <= n; k += k & -k) maxn[k] = max(maxn[k], x);
} ll query(int k) {
ll res = 0;
for (; k; k -= k & -k) res = max(res, maxn[k]);
return res;
}
} bit;
int main() {
cin.tie(nullptr);
ios::sync_with_stdio(false);
for (cin >> t; t; --t) {
ans = sum = 0;
bit.clear();
cin >> n;
for (int i = 1; i <= n; ++i) cin >> a[i];
for (int i = 1; i <= n; ++i) cin >> c[i], sum += c[i];
copy(a + 1, a + n + 1, d + 1);
sort(d + 1, d + n + 1);
int tot = unique(d + 1, d + n + 1) - d - 1;
for (int i = 1; i <= n; ++i)
a[i] = lower_bound(d + 1, d + tot + 1, a[i]) - d;
for (int i = 1; i <= n; ++i) {
ll cur = bit.query(a[i]) + c[i];
ans = max(ans, cur);
bit.update(a[i], cur);
} cout << sum - ans << '\n';
} return 0;
}
至此,恭喜你已经学会了最长不降子序列问题的树状数组优化!
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...