【题解】AT_abc290_c 题解

AT_abc290_c 题解

思路分析

一道挺不错的思维题。

首先我们要证明两个结论:

  1. 对于长度为 kk 的数组 XXMEX(X)MEX(X) 不超过 kk
  2. 答案为 00k1k-1 中最小的在数组中未出现的数。如果不存在,就为 kk

对于第一个结论,设 MEX(X)=pMEX(X) = p。由于 00p1p-1 都要在 XX 中出现,共 pp 个数。所以 pp 应当不超过 kk,证毕。

对于第二个结论,令答案为 pp。答案要求 00p1p-1 均在数组中。如果不在,就一定不连续。我们为了连续,就要从数组分离出一段连续的 00p1p-1 。而由于枚举的是 00k1k-1 ,如果有数不存在数组中,说明不连续,否则能一一对应。所以,找到了最小的未出现的数 pp,就说明前面的 00p1p-1 一定存在,否则不是最小。而如果 00k1k-1 都不是,则 00k1k-1 都存在于数组中。又由结论一,只能为 kk,证毕。

最后,按照结论二给出的方式实现即可。由于只需要判断存在性,与某个数的个数无关,所以可以使用 STL 函数 unique 去重优化时间复杂度。又由于需要判断一个较长的数组中某个数的存在性,而数组单个数可能很大,无法用桶。所以可以排序去重后使用二分搜索来判断。

代码

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 3e5 + 10;

int a[N];

bool findex(int nx, int num) //findex(nx, num) 指在去重后长度为 nx 的数组中判断 num 是否存在。
{
	int l = 1, r = nx; //左右指针分别数组的首尾。 
	while(l <= r)
	{
		int mid = (l + r) >> 1; //取中间。 
		if(a[mid] == num) //找到了。 
		{
			return true;
		}
		else if(a[mid] > num) //如果比待找数大, 
		{
			r = mid - 1; //缩小范围,范围移到左半部分。 
		}
		else //否则比待找数小,
		{
			l = mid + 1; //缩小范围,范围移到右半部分。
		}
	}
	return false;
}

int main()
{
	int n, k;
	cin >> n >> k;
	for(int i = 1;i <= n;i++) cin >> a[i];
	sort(a + 1, a + n + 1); //unique 去重前要排序。 
	int len = unique(a + 1, a + n + 1) - a; //unique 返回的是尾指针,需要减去头指针计算长度。
	for(int i = 0;i < k;i++) //枚举 0 到 k - 1 
	{ 
		if(!findex(len, i)) //如果不存在, 
		{
			cout << i << endl; //就输出并结束程序,即可保证最小。 
			return 0;
		}
	} 
	cout << k << endl; //如果都存在,输出 k。 
	return 0;
}
posted @ 2023-02-22 22:47  邻补角-SSA  阅读(20)  评论(0)    收藏  举报  来源