Codeforces 2123E MEX Count
题目
原题链接 2123E MEX Count
定义一个数组的 值为:数组中最小的未出现的非负整数。例如:
- ,因为 不在这个数组中。
- ,因为 和 在数组中但 不在。
- ,因为 都在数组中,但 不在。
给定一个含 个非负整数的数组 ,对于所有 ,计算从 中移除 个值后 可能的取值个数。
输入
第一行为一个整数 ,表示数据组数。接下来是 组数据。对于每组数据:
第一行为一个整数 ,表示数组的大小。
第二行为 个整数,。
保证所有 之和不超过 。
输出
对于每组数据,输出一行,包含 个整数,分别表示移除 个值后 可能的取值个数,其中 。
样例
输入 #1
5
5
1 0 0 1 2
6
3 2 0 4 5 1
6
1 2 0 1 3 2
4
0 3 4 1
5
0 0 0 0 0
输出 #1
1 2 4 3 2 1
1 6 5 4 3 2 1
1 3 5 4 3 2 1
1 3 3 2 1
1 1 1 1 1 1
分析
首先很容易发现删除 个值(即不删除值)时 值是唯一的。
此时要么 并且 ;要么 。后一种情况下答案显然全是 ,后面忽略。
然后考虑 的情况。
对于 中所有小于 的值,如果出现的次数不多于 ,则都可以被删完,每删完一个值都会产生一个新的 。
出现次数多于 的数和大于 的数,删除后都不影响 。
但这并不完全正确。例如考虑 的情况,删除多于一个数都会让最初的 无法取到,也就是说删除后不影响 值的数不够删了(此例中没有删除后不影响 值的数),因此还要统计一下这些数的个数。在 较大时,这些数可以作为缓冲,如果把这些数删完后 还有剩余,则需要相应减少 的可能数。
代码实现
由于 中数值范围较小,可以直接统计一下出现次数:
vector<int> v(n + 1);
for (int i = 1; i <= n; i++) {
int x;
cin >> x;
v[x]++;
}
然后计算 。 表示出现次数小于等于 的数的个数, 和 分别表示下小于和大于 的数的个数, 为删除后不影响 值的数的个数。
int p, l{};
vector<int> b(n + 1);
for (p = 0; p <= n; p++) {
if (!v[p]) break;
b[v[p]]++;
l += v[p];
}
for (int i = 1; i <= n; i++) {
b[i] += b[i - 1];
}
int r = n - l, s1 = l - p;
int u = s1 + r;
最后当 时答案为 ,否则为 。
根据变量间的关系,表达式实际上还可以化简,只需要知道 的值即可。
完整代码
using namespace std;
void solve() {
int n;
cin >> n;
vector<int> v(n + 1);
for (int i = 1; i <= n; i++) {
int x;
cin >> x;
v[x]++;
}
int p;
vector<int> b(n + 1);
for (p = 0; p <= n; p++) {
if (!v[p]) break;
b[v[p]]++;
}
for (int i = 1; i <= n; i++) {
b[i] += b[i - 1];
}
int u = n - p;
cout << "1";
for (int k = 1; k <= n; k++) {
cout << ' ';
int tmp = u - k;
if (tmp >= 0) {
cout << b[k] + 1;
} else {
cout << n - k + 1;
}
}
cout << '\n';
}
int main() {
cin.tie(0)->sync_with_stdio(0);
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}