【Codeforces Global Round 9 D】Replace by MEX
题目链接
翻译
给你一个长度为 \(n\) 的数组。
你可以将某个位置上的数字换成 \(mex\) 最多 \(2*n\) 次。
让你把这个数组变成不下降的,这个数组中的数字都在 \(0..n\) 之间。
\(mex\) 表示没有出现过的最小的整数。
题解
构造题,我们可以把目标改为让最后的数组变成 \(0,1,2\cdots n-1\)。
可以这样做,一开始肯定有很多位置 \(a[i]!=i\) (这里 \(0\le i\le n-1\)),设每次求出来的 \(mex\) 为 \(tag\)。
那么如果 \(tag<n\), 则直接让 \(a[tag]=tag\) 即可,因为 \(tag\) 是没有出现过的数字,所以 \(a[tag]!=tag\)。
这样我们就能让 \(a[i]!=i\) 的个数减小 \(1\) 了。
但是可能会出现 \(tag=n\) 的情况,这个时候,我们随便找个 \(a[i]!=i\) 的位置 \(i\), 让 \(a[i] = tag\) 就行。
则下一次的 \(mex\) 一定是小于 \(n\) 的了。
再重复让 \(a[tag] = tag\) 即可。
显然不会超过 \(2*n\) 次这样的操作。
代码
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;
const int N = 1000;
int t, n;
int a[N + 10],b[N + 10];
int getMex(){
memset(b,0,sizeof(b));
for (int i = 0;i < n; i++){
b[a[i]] = 1;
}
for (int i = 0;i <= n; i++)
if (b[i] == 0){
return i;
}
return 0;
}
int main(){
scanf("%d",&t);
while (t--){
vector<int> v;
v.clear();
scanf("%d",&n);
int cnt = 0;
for (int i = 0;i < n; i++){
scanf("%d",&a[i]);
if (a[i] != i){
cnt++;
}
}
while (cnt > 0){
int tag = getMex();
if (tag < n){
a[tag] = tag;
v.push_back(tag+1);
cnt--;
}else{
for (int i = 0;i < n; i++)
if (a[i] != i){
a[i] = tag;
v.push_back(i+1);
break;
}
}
}
printf("%d\n",(int)v.size());
for (int i = 0;i < (int)v.size(); i++){
printf("%d ",v[i]);
}
puts("");
}
return 0;
}