CF1867C Salyg1n and the MEX Game

发布时间 2023-09-12 12:03:30作者: One_JuRuo

思路

看着无从下手,实际上又是一道诈骗题。

假设原数列不存在 \(0\),那么我们可以直接加入 \(0\),然后游戏结束,假设答案是 \(k\)。那么,如果我们选择加入 \(k\),来试图让答案变大,那么 Bob 就会移除一个数,最优的话是 \(1\),这样的话,你无论加入 \(1\) 还是 \(0\),答案都不会超过 \(1\),于是一手好牌就被你打没了,就算 Bob 不移除 \(1\),移除 \(x\),你的最终答案也不会超过 \(x\) 了。

(因为 Bob 只能移除比你加入的数还要小的数,所以如果你操作不好,Bob 甚至还没法让你)

那么,我们直接猜测最优的操作是每次加入目前集合中不存在的最小的数。

蒟蒻大概证明一下:

假设目前最小的不存在的数是 \(x\),那么 \([0,x)\) 都应该是存在的,那么加入以后 \([0,x]\) 都是存在的,答案为 \(x+1\),此时 Bob 会移除中间的某个数,那么我们再加回来,直到 Bob 移除了 \(0\),然后你添加了 \(0\),游戏结束。

但是如果你不加入 \(x\),那么,Bob 再随便移除一个数 \(y\),这时,如果你使用之前的策略,答案也只是 \(x\),不使用的话,Bob 再移除一个小于 \(y\) 的数,答案将会再次变小,你就会无力回天了。

所以想要答案尽可能的大,我们就只能每次选择最小的不存在的数。

蒟蒻在开始想的是拿个 set 存,但是,显然 set 的常数是接受不了的,可以想象,如果数据是 \([0,n-1]\),那么 Bob 就可以和你扯大约 \(n\) 次皮,加上 set \(\log n\) 的复杂度,轻松让你 TLE

当然,也可能是我操作 set 有问题,因为我是把前 \(10^5\) 都统计进了一个 set,然后再转一遍到另一个 set,并且赛后才发现给的是有序的,连最开始的 set 也不需要。(果然 OI 学多了,会掉视力)

然后蒟蒻转念一想,如果最开始加入 \(x\),那么 Bob 无论取走那个数,下一次最小的都一定是 Bob 取走的那个数。

原来这么简单,赛时交了一发 TLE 一定是我脑抽了。

AC code

#include<bits/stdc++.h>
using namespace std;
int T,n,a,p;
set<int>s;
int main()
{
	scanf("%d",&T);
	while(T--)
	{
		cin>>n,s.clear();
		for(int i=1;i<=n;++i) cin>>a,s.insert(a);
		for(int i=0;i<=100000;++i) if(!s.count(i)){p=i;break;}//这里只是为了方便找到第一次的答案,所以用了set
     //其实不需要的,因为给的本身就是有序的,但是赛时没看到,现在又懒得改了。。。
		while(1)
		{
			cout<<p<<endl;//上文是指这里用了set
			cin>>a;
			if(a==-1||a==-2) break;
			p=a;
		}
	}
	return 0;
}