CF1867C Salyg1n and the MEX Game
思路
看着无从下手,实际上又是一道诈骗题。
假设原数列不存在 \(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;
}