题解:CF1413D Shurikens
若顺序、在线地处理每个事件,实时维护柜台中的剑的价格组成的集合 \(A\):
- 对于
+事件:指定一个尚未被指定过的价格 \(v \in [n]\),将 \(v\) 加入 \(A\)。 - 对于
- x事件:若满足 \(\min(A) = x\),则从 \(A\) 中删除 \(x\),报告成功;否则报告失败。
目标是对于每个 + 事件指定合适的 \(v\),使得每个 - x 事件都成功。
这是困难的——一个 + 的 \(v\) 的取值往往取决于后发生的 - x 事件的 \(x\) 的取值。而若从一个 - x 事件的角度考虑,则在此事件发生之前,发生过这样的 + 事件是必要的:
- 此
+事件的 \(v\) 被指定了 \(x\)。
我们称这样的 + 事件与此 - x 事件的关系是互相“配对“的。在题目的限制下,能与一个事件配对的事件是唯一的,且最多只可能有一个配对的事件;目标是使得每个事件都恰好有一个配对的事件。
既然能与 - x 事件配对的 + 事件总是在 - x 发生前,考虑倒序处理每个事件,并实时维护一个集合 \(S\)。假设当前处理到倒数第 \(i\) 个事件,则 \(S\) 存放着:所有尚未找到与其配对的 + 事件的 - x 事件的所有的 \(x\)。
用一段伪代码详细讲解如何维护:
倒序遍历每个事件:
-
若当前事件为
+- 若 \(S\) 为空
-
报告无解
因为不存在可与其配对的
- x事件了。
-
- 否则
-
将 \(v\) 指定为 \(\min(S)\),并从 \(S\) 中删除 \(v\)
首先总要选一个后发生的
- x事件配对,而且选哪个都是能配对的,直接把 \(v\) 设置成想配对的- x的 \(x\) 即可。但选其中 \(x\) 最小的,是最“优”的。这里的“优”定义为,若这样做不可行,没有任何一种其他方法是可行的。注意在处理
- x时,判断是否无解的依据是 \(\min(S)\) 是否小于 \(x\)。若删除的是 \(\min(S)\),那么删除后新的 \(\min(S)\) 必然变大;对于固定的 \(x\),要么使得 \([\min(S) < x]\) 的结果不变,要么使得 \([\min(S) < x]\) 从 \(0\) 变为 \(1\)。
否则 \([\min(S) < x]\) 一定不变。
而且对任何时刻下的 \(S\) 都是如此,不受做过的决策影响。因此删除 \(\min(S)\) 一定是最“优”的。
-
- 若 \(S\) 为空
-
否则当前事件为
- x- 若 \(S\) 不为空且 \(\min(S) < x\)
-
报告无解
意味着在当前事件发生后,还发生过一个仍未找到与其配对的
+事件的- x事件,且其 \(x\),即能与其配对的+事件的 \(v\),是 \(\min(S)\),小于当前事件的 \(x\)。这会导致,若在当前事件发生前的一个
+事件的 \(v\) 被指定了 \(\min(S)\),会使得当前事件的 \(x\) 必定无法成为 \(\min(S)\),无法被成功删除;而在当前事件发生后,又没有+事件能与其配对。因此矛盾,必定无解。
-
- 否则
- 将 \(x\) 加入 \(S\)
- 若 \(S\) 不为空且 \(\min(S) < x\)
插入、查询最小值、删除最小值——可以使用堆维护 \(S\) 做到 \(O(n \log n)\)。
注意到若有解,每次处理 - x 事件时,只需要做一件事:将 \(x\) 加入 \(S\);而此时必有 \(x < \min(S)\)。因此只需要维护一个栈,要加入一个数时直接将其压入作为栈顶,就可以保证栈是从栈底至栈顶单调递减的了。查询最小值,即查询栈顶;删除最小值,即删除栈顶。
时空复杂度 \(\Theta(n)\) 的参考实现:
#include <bits/stdc++.h>
using namespace std;
constexpr int N = 100000 + 1;
char op[N + N];
int x[N + N], stk[N], ans[N];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
for (int i = 1; i <= n + n; ++i) {
cin >> op[i];
if (op[i] == '-') {
cin >> x[i];
}
}
int p = n, top = 0;
for (int i = n + n; i; --i) {
if (op[i] == '+') {
if (!top) {
cout << "NO\n";
return 0;
}
ans[p--] = stk[top--];
} else {
if (top && stk[top] < x[i]) {
cout << "NO\n";
return 0;
}
stk[++top] = x[i];
}
}
cout << "YES\n";
for (int i = 1; i <= n; ++i) {
cout << ans[i] << ' ';
}
cout << '\n';
return 0;
}
posted on 2026-01-06 16:35 SkyWave2022 阅读(16) 评论(0) 收藏 举报
浙公网安备 33010602011771号