1. 程式人生 > 實用技巧 >[CF1375D] Replace by MEX - 構造

[CF1375D] Replace by MEX - 構造

Description

給定一個長度為 \(n\) 的數列,可以進行不超過 \(2n\) 次操作,每次可以選擇一個位置,將它的值設為當前整個數列的 mex。構造一種操作方案,使得整個數列的值單調不降。

Solution

我們考慮將整個數列做成 \(0,1,2,...,n-1\) 的形式。我們給出一種構造方案使得能在不超過 \(2n\) 步內將任何一個序列做成這種形式。

當整個數列的 mex 小於 n 時,執行 A 類操作,即將 \(a[mex+1]\) 賦值為 \(mex\);否則,執行 B 類操作,任意找一個不滿足 \(a[i]=i-1\) 的位置,將 \(a[i]\) 賦值為 \(mex=n\)

顯然每一個 B 類操作後一定緊跟著一個 A 類操作,而一個 A 類操作會搞定一個位置,並且這個位置以後再也不會被操作到。

#include <bits/stdc++.h>
using namespace std;

#define int long long

int Mex(vector<int> vec)
{
	vector<int> tmp(vec.size() + 2, 0);
	for (auto i : vec)
		if (1ull * i < 1ull * vec.size())
			tmp[i]++;
	int ans = 0;
	while (tmp[ans])
		++ans;
	return ans;
}

void solve()
{
	int n;
	cin >> n;

	vector<int> a(n + 2, 0);
	for (int i = 1; i <= n; i++)
		cin >> a[i];

	vector<int> ans;

	while (true)
	{
		vector<int> tmp;
		for (int i = 1; i <= n; i++)
			tmp.push_back(a[i]);
		int mex = Mex(tmp);
		if (mex == n)
		{
			int pos = 0;
			for (int i = 1; i <= n; i++)
				if (a[i] != i - 1)
				{
					pos = i;
					break;
				}
			if (pos)
			{
				a[pos] = n;
				ans.push_back(pos);
			}
			else
			{
				break;
			}
		}
		else
		{
			a[mex + 1] = mex;
			ans.push_back(mex + 1);
		}
	}

	cout << ans.size() << endl;
	for (int i : ans)
		cout << i << " ";
	cout << endl;
}

signed main()
{
	ios::sync_with_stdio(false);

	int t;
	cin >> t;
	while (t--)
	{
		solve();
	}

	return 0;
}