1 条题解
-
0
题目题解
问题理解
给定长度为 的数组 (元素值在 之间)。
对于每个 ,统计从 中恰好移除 个元素后,剩余数组的 MEX 可能取值的个数。
第一步:原始 MEX
设 为原数组的 MEX。即:
由于移除元素只能移除某些值,不能引入新值,因此剩余数组的 MEX 一定 。
此外,MEX 不能超过剩余数组的长度。设剩余数组长度为 ,则
第二步:MEX 等于某个 的条件
考虑 。
要使剩余数组的 MEX 等于 ,必须:- 所有小于 的数在剩余数组中至少出现一次(这样 才是最小的缺失值)。
- 数字 在剩余数组中不出现(因为缺失它)。
由于原数组中每个小于 的数都至少出现一次(否则 ),我们可以保留它们各一个。
要确保 不出现,必须移除所有 的实例。设 为 在原数组中的出现次数,则此外,剩余数组长度为 ,其中我们已经保留了 个不同的数(),因此
所以 可行的条件是:
第三步:充分性证明
若上述条件成立,我们可以构造剩余数组如下:
- 从每个 中保留一个元素(共 个)。
- 从剩余元素(不包括 )中任意选择 个,保证总数达到 。
由于 ,我们移除了所有 ,且剩余元素足够多(因为 ),因此构造可行。
第四步:差分数组方法
设 为移除 个元素后可能的 MEX 值的个数。
对于每个 (),它对 的贡献是:当 在区间 内时, 是可行的。
因此我们可以用差分数组 来统计:
- 在 处 (开始可行)
- 在 处 (结束可行)
初始 全为 。
$$diff[\text{freq}(i)] \mathrel{+}= 1, \quad diff[n - i + 1] \mathrel{-}= 1. $$
对所有 执行:然后前缀和得到 :
注意 时,由于原数组中缺少 ,不可能使 MEX 大于 ,因此不考虑。
最终算法
- 统计每个值的出现次数 。
- 找到原数组的 MEX (最小的未出现值)。
- 初始化差分数组 长度为 。
- 对 到 :
- $diff[\text{freq}[i]] \leftarrow diff[\text{freq}[i]] + 1$
- 计算前缀和得到 ,并输出。
时间复杂度
- 统计频率:。
- 处理 个 :。
- 总复杂度 ,满足 。
代码实现
#include <bits/stdc++.h> using namespace std; void solve() { int n; cin >> n; vector<int> a(n); vector<int> freq(n + 1, 0); for (int i = 0; i < n; i++) { cin >> a[i]; if (a[i] <= n) freq[a[i]]++; } // 找到原数组的 MEX int mex = 0; while (freq[mex] > 0) mex++; // 差分数组 vector<int> diff(n + 2, 0); for (int i = 0; i <= mex; i++) { diff[freq[i]]++; diff[n - i + 1]--; } // 前缀和得到答案 vector<int> ans(n + 1, 0); int cur = 0; for (int k = 0; k <= n; k++) { cur += diff[k]; ans[k] = cur; } // 输出 for (int k = 0; k <= n; k++) { cout << ans[k] << (k == n ? "\n" : " "); } } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) { solve(); } return 0; }
验证样例
对于第一个测试用例:
- ,
- , , , →
- 对 : ,
对 : ,
对 : ,
对 : ,
计算前缀和得到: ,与样例一致。
总结
本题的关键在于:
- 将问题转化为对每个 判断 的可行区间 。
- 使用差分数组高效维护 的变化。
- 注意 MEX 不能超过原始 MEX 和剩余数组长度。
- 1
信息
- ID
- 6240
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 3
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者