题目链接
翻译
给你一个长度为 (n) 的数组。
你可以将某个位置上的数字换成 (mex) 最多 (2*n) 次。
让你把这个数组变成不下降的,这个数组中的数字都在 (0..n) 之间。
(mex) 表示没有出现过的最小的整数。
题解
构造题,我们可以把目标改为让最后的数组变成 (0,1,2cdots n-1)。
可以这样做,一开始肯定有很多位置 (a[i]!=i) (这里 (0le ile 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
",(int)v.size());
for (int i = 0;i < (int)v.size(); i++){
printf("%d ",v[i]);
}
puts("");
}
return 0;
}