1 条题解
-
0
题目题解
问题理解
给定一个由互不相同整数组成的数组 。
允许两种操作:- 选择一个非空前缀,将其替换为该前缀的最小值。
- 选择一个非空后缀,将其替换为该后缀的最大值。
对于每个 ,判断是否能通过一系列操作将数组变为仅包含 的单一元素数组 。
第一步:基础情况
当 时,两种操作都可以得到单一元素:
- 选择整个数组作为前缀,得到 。
- 选择整个数组作为后缀,得到 。
因此,对于 ,两个元素都可以成为最终元素。
第二步:充分条件
若 是前缀最小值,则存在操作序列将其变为 。
构造方法:
- 选择前缀 ,将其替换为最小值 。
此时数组变为 。 - 若数组长度 ,选择后缀 (即除了第一个元素外的所有元素),将其替换为最大值。
此时数组变为 ,其中 。 - 现在数组长度为 ,根据第一步的结论,可以将其变为 。
同理:若 是后缀最大值,也可通过对称操作变为 。
第三步:必要条件
假设 既不是前缀最小值,也不是后缀最大值。
设:- 是前缀 中最小值 的位置。
- 是后缀 中最大值 的位置。
显然 。
断言:不可能在不移除 的情况下移除 或 。
证明:考虑第一次操作移除 、 或 中的至少一个。
-
若通过选择前缀移除 :
该前缀必须包含一个比 更小的元素。由于 是 的最小值,该前缀必须包含 之后的元素。但此时该前缀也包含 ,而 不是该前缀的最小值(因为有比它小的 存在),因此 也会被移除。 -
若通过选择后缀移除 :
该后缀必须包含 ,因此也包含 和 。由于 不是该后缀的最大值(因为有更大的 ), 也会被移除。
对称地,若第一次操作移除 ,同样会导致 被移除。
因此,任何试图移除 或 的操作都会同时移除 。
故无法在不移除 的情况下单独保留它,从而不可能将数组变为 。
最终结论
$$\text{ans}_i = \begin{cases} 1, & \text{if } a_i = \min(a_1, \dots, a_i) \text{ 或 } a_i = \max(a_i, \dots, a_n), \\ 0, & \text{otherwise}. \end{cases} $$
算法步骤
- 预处理前缀最小值数组
pre[i] = min(a[1..i])。 - 预处理后缀最大值数组
suf[i] = max(a[i..n])。 - 对于每个 ,若
a[i] == pre[i]或a[i] == suf[i],输出'1',否则输出'0'。
时间复杂度
- 预处理 。
- 总复杂度 ,满足要求。
代码实现
#include <bits/stdc++.h> using namespace std; void solve() { int n; cin >> n; vector<int> a(n + 1); for (int i = 1; i <= n; i++) { cin >> a[i]; } vector<int> pre(n + 1, INT_MAX); vector<int> suf(n + 2, INT_MIN); for (int i = 1; i <= n; i++) { pre[i] = min(pre[i - 1], a[i]); } for (int i = n; i >= 1; i--) { suf[i] = max(suf[i + 1], a[i]); } for (int i = 1; i <= n; i++) { if (a[i] == pre[i] || a[i] == suf[i]) { cout << '1'; } else { cout << '0'; } } cout << '\n'; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) { solve(); } return 0; }
验证样例
测试用例 数组 前缀最小值 后缀最大值 结果 1 2 3 与题目输出一致。
总结
本题的关键在于:
- 识别出前缀最小值和后缀最大值是可行的充分条件。
- 证明非前缀最小值且非后缀最大值的元素一定不可行(通过引入左侧更小值和右侧更大值,并分析首次操作)。
- 最终得到简单的 判断方法。
- 1
信息
- ID
- 6235
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 3
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者