MEX Count
题目
换个角度考虑, 什么情况下对答案有贡献
一、核心条件:MEX=i 的充要条件
对于某个非负整数 i 要成为删除 k 个元素后的 MEX,必须满足两个条件(Step 2):
-
i ≤ n−k:
剩余元素数量为 n−k,而 MEX 不能超过数组大小(否则无法容纳 0~i-1 这些数)。例如,剩余 3 个元素的 MEX 最大为 3(如 [0,1,2])。 -
freq(i) ≤ k:
必须删除 i 的所有出现(否则 i 仍在数组中,MEX 会小于 i)。因此,删除的 k 至少要等于 i 的出现次数 freq(i)。
二、区间标记:每个 i 的有效 k 范围
对于每个 i(0 ≤ i ≤ mex,其中 mex 是原始数组的 MEX),其有效 k 范围是 闭区间 [L, R]:
L = freq(i)(最小删除次数,确保删完i)
R = n−i(最大删除次数,确保剩余元素足够大,容纳0~i-1)
若 L ≤ R,则 i 可以在 k ∈ [L, R] 时成为 MEX。我们通过 差分数组 标记这些区间,后续用前缀和统计每个 k 对应的有效 i 数量。
Code
点击查看代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int MAXN = 2e5 + 10;
int t;
int freq[MAXN]; // 统计每个数的出现次数
int diff[MAXN+2]; // 差分数组,用于区间标记
void solve() {
ll n;
cin >> n;
memset(freq, 0, sizeof(freq)); // 重置频率数组
for (int i = 1; i <= n; ++i) {
int x;
cin >> x;
if (x <= n) { // 超过n的数不影响MEX计算,可忽略
freq[x]++;
}
}
// 步骤1:计算原始MEX(第一个未出现的非负整数)
int mex = 0;
while (mex <= n && freq[mex] > 0) {
mex++;
}
// 步骤2:初始化差分数组,标记有效区间
memset(diff, 0, sizeof(diff));
// 处理 0 ~ mex-1(这些数一定存在,因为mex是第一个缺失的)
for (int i = 0; i < mex; ++i) {
int L = freq[i]; // 删完i至少需要删除L次
int R = n - i; // 剩余元素至少i个(容纳0~i-1)
if (L > R) continue; // 区间无效,跳过
diff[L]++; // 标记区间起点
if (R + 1 <= n) {
diff[R + 1]--; // 标记区间终点+1
}
}
// 处理 mex 本身(仅当剩余元素足够大时,mex可作为MEX)
if (mex <= n) {
int L = 0; // 删0次即可(mex原本就不存在)
int R = n - mex; // 剩余元素至少mex个(容纳0~mex-1)
diff[L]++;
if (R + 1 <= n) {
diff[R + 1]--;
}
}
// 步骤3:前缀和计算每个k的MEX可能值数量
vector<int> res(n + 1, 0);
int current = 0;
for (int k = 0; k <= n; ++k) {
current += diff[k];
res[k] = current;
}
// 输出结果
for (int k = 0; k <= n; ++k) {
cout << res[k] << " ";
}
cout << "\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> t;
while (t--) {
solve();
}
return 0;
}

浙公网安备 33010602011771号