【做题记录】CF1375D Replace by MEX

Problem

CF1375D Replace by MEX

题目大意:
给你一个长度为 \(n\),值域在 \([1,n]\) 的序列 \(a\),每次操作可以将一个位置的数替换为当前序列的 \(MEX\),请你让序列变成一个单调不降的序列,要求操作数在 \(2n\) 以内。
输出操作数和每次操作对应的位置,不要求操作数最少。

Solution

既然不要求操作数最少,那么我们就可以尝试构造一个特殊的序列,比如 \(0,0,\cdots,0\)\(0,1,\cdots,n-1\)\(1,2,\cdots,n\) 等。
第一种实现太难了些,所以我们尝试后两种。
\(1,2,\cdots,n\) 为例。
首先,当一个数符合要求后,我们显然不会去改它。就算是出现了其他数都符合要求(指满足我们想要的结果,下同),剩一个数不同的情况,我们只要先让他变成当前的 \(MEX\),如果当前 \(MEX\) 刚好是我们想要的,就直接赋值完事,否则的话这个 \(MEX\) 一定为 \(0\),那么赋值之后就又变回第一种情况了,因为其他的数都已经把前面的那些数都填掉了。
接下来我们对 \(MEX\) 分类讨论。

\(MEX \not= 0\) 时,我们直接让 \(a_{MEX}=MEX\) 即可。
\(MEX = 0\) 时,我们就把他赋到一个不符合要求的数中去。

这样最后一定能得到我们想要的序列,接下来来检验他能否在 \(2n\) 次操作内完成。
显然一个数最多只会被操作两次。
因为当把这个数赋值为 \(0\) 后,除非让他符合要求,否则不会改变他,因为这时 \(MEX\) 不可能会等于 \(0\)(只有等于 \(0\) 时才可能对他再做一次操作)。
于是操作次数最多为 \(2n\),符合题意。

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int T,n,a[1005];
bool over[1005];//over[i]表示a[i]是否符合要求
int cnt,len,ans[2005];//cnt存有多少个数符合要求,len和ans存答案。
int main()
{
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d",&n);
		cnt=len=0;
		memset(over,false,sizeof(over));
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&a[i]);
			if(a[i]==i) over[i]=true,cnt++;
		}
		while(cnt<n)
		{
			int mex;
			bool m[1005];
			memset(m,0,sizeof(m));
			for(int i=1;i<=n;i++) m[a[i]]=true;
			for(int i=0;i<=n;i++)
				if(!m[i]){mex=i;break;}
			if(mex==0)
			{
				for(int i=1;i<=n;i++)
					if(!over[i]){a[i]=0;ans[++len]=i;break;}
				continue;
			}
			a[mex]=mex;
			over[mex]=true;
			ans[++len]=mex;
			cnt++;
		}
		printf("%d\n",len);
		for(int i=1;i<=len;i++) printf("%d ",ans[i]);
		puts("");
	}
	return 0;
}
posted @ 2020-11-26 19:24  Mine_King  阅读(219)  评论(0编辑  收藏  举报