1. 程式人生 > 實用技巧 >【做題記錄】CF1375D Replace by MEX

【做題記錄】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;
}