【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;
}
posted @ 2020-07-09 23:07  AWCXV  阅读(123)  评论(0编辑  收藏  举报