题意
给一个(n)个元素的数组(a_i),每次可以有两种操作:
- 将所有数中最大的那个数整个删除
- 将所有数减(1),如果减到(0)就删除
问如果两个人轮番操作,到最后谁不能进行操作了就输,问谁能赢。
- (1leq Nleq 10^5)
- (1leq a_i leq 10^9)
分析
我们考虑,将整个数组从大到小排序,然后考虑像这样可以画出一个图表一样的东西,那么就必然每次删最大相当于是将最左边的删掉。然后我们考虑全部数减(1)的操作,这相当于是将最下面一行删掉。这个时候这个游戏就变成了一个更加简洁的东西:给一条单调不增的折线,每次可以往上和往右走,走到边界的时候输,问怎样胜利。这个问题很显然可以设例如在先手的视角,对于某一个坐标((x,y)),设出一个判定函数(f(x,y))表示从((x,y))出发是必胜还是必败。很好写出转移,当((x+1,y))和((x,y+1))两个中存在一个必败的,那当前这个就是必胜的(只要走到那个必败的格子,后手就必败),反之当前格子必胜。
这样暴力计算显然是( ext O(sum a_i))的,复杂度爆炸所以没啥用= =但是你要是打个表出来,你会发现一个奇妙的规律:从((0,0)),((1,1)),(cdots),((v,v))一直都是同样的一个胜利情况!我们考虑这个结论其实同样很容易看出,考虑对于某一方,如果任意一个((x+1,y+1))是必败状态,那么((x,y+1))和((x+1,y))都是必胜状态,进一步((x,y))是必败状态。于是我们可以推出,在对这一方来说,这样一条斜向上的线上面的胜负状态都是一样的,那么对于另一方因为是刚好反过来所以也是一样的。
我们考虑,我们走某个点刚好顶住了,这时我们可能有两种方式:一种是往上走,一种是往右走。这两个都很容易计算,我们只要看一下往两个方向走能够顶到什么点,那个点的胜负状态是怎样的就可算出答案,整体复杂度也显然是( ext O(N))的。
# include <bits/stdc++.h>
# define rep(i,a,b) for(int i=a;i<=b;++i)
using namespace std;
const int maxn = 100010;
int read(){
char c=getchar(); int x=0;
for(;c<'0'||c>'9';c=getchar());
for(;c>='0'&&c<='9';c=getchar())x=x*10+c-'0';
return x;
}
int a[maxn];
int n;
int main(){
n=read();
rep(i,0,n-1)a[i]=read();
sort(a+0,a+n);
reverse(a+0,a+n);
int p=0,y=0;
for(;p+1<n&&a[p+1]>p+1;++p);
for(;y<n&&a[y]>p;++y);
if ((a[p]-p)&1 && (y-p)&1) puts("Second");
else puts("First");
return 0;
}