题意翻译

桌上有 $n$ 堆糖果 , 其中第 $i$ 堆糖果有 $a_i$ 个糖。

Alice 与 Bob 在玩游戏,轮流进行,每次进行下列两个操作中的一个:

  • 将当前糖数最多的那堆糖果全部吃完

  • 将每堆糖果吃掉一个

吃掉最后一颗糖果的人败者,假设两人足够聪明,问先手还是后手必胜?


输入格式

第一行有一个整数 $n$ 表示糖果的堆数。

接下来一行有 $n$ 个正整数,其中第 $i$ 个整数 $a_i$ 表示第 $i$ 堆糖果的数目。


输出格式

输出First(表示第一个人必胜),或Second(表示第二个人必胜)


Input & Output ‘s examples

Input ‘s eg 1

3
1 2 3

Output ‘s eg 1

Second

Input ‘s eg 2

3
1 2 1

Output ‘s eg 2

First

数据范围与约定

对于 $100\%$ 的数据,满足 $1 \leq n \leq 10^5 , 1 \leq a_i \leq 10^9$。


分析

思维难度较大的博弈。

题目中的第一个操作要求取走当前最多的糖果,且第二种操作不会改变糖果的相对大小,故我们先将数列 $a$ 进行降序排序。

举个栗子,设 $a = (7 , 6 , 7 , 4 , 7 , 4 , 2 , 4 , 2)$,则排过序之后就成了下面的样子:

a = (7 , 6 , 7 , 4 , 7 , 4 , 2 , 4 , 2)排序之后的结果

考虑两种操作的本质。不难发现,消去最大值的实质就是消去最左边的一行,而全局减一的实质就是消去最下面的一行

两种操作的实质

之后将其转换为网格图,设左下角为 $(0 , 0)$,就有了下面那张图。

想想上面两种操作在网格图上相当于什么?不难发现,消去最大值相当于在当前位置向右移一格,而全局减一相当于向上移一格

网格图

显然,网格图的边界就相当于最后一颗糖果,为必败态。

根据引理可知:对于一场游戏,假设先后手公平,那么一个状态为必胜,当前仅当其能转移的状态有一个为必输;而一个状态为必输,当前仅当其能转移的状态均为必赢

把引理套到题目上,就可知对于任意一个不在边界上的点,如果它的上面和右面都是必败点,则这个点是必胜点;否则为必败点。

知道这个之后我们就可以建出这个网格图了(图中以○表示必败点,×表示必胜点)。

建出后的网格图

之后看一下原点是必胜还是必败,若为必胜则后手必胜,反之后手必败(因为先手会转移至原点右边或上边的点,即 $(1 , 0)$ 或 $(0 , 1)$。故这两个点才是先手的状态点)。

但这样建图复杂度为 $O(n^2)$ 的,无法通过本题。

考虑找规律,不难发现(不难个鬼)除边界外,图上对角线的胜败状态一致。

故我们可以找到以原点为左下角的最大正方形,之后看其右上角到两个边界的距离,若其中一个为奇数则该点为必败点,否则为必胜点

规律及做法


Code[Accepted]

#include <algorithm>
#include <iostream>
#include <cstring>
#include <iomanip>
#include <cstdlib>
#include <climits>
#include <cstdio>
#include <bitset>
#include <string>
#include <vector>
#include <cctype>
#include <stack>
#include <queue>
#include <deque>
#include <cmath>
#include <map>
#include <set>

#define I inline
#define fi first
#define se second
#define pb push_back
#define MP make_pair
#define PII pair<int , int>
#define PSL pair<string , long long>
#define PLL pair<long long , long long>
#define all(x) (x).begin() , (x).end()
#define copy(a , b) memcpy(a , b , sizeof(b))
#define clean(a , b) memset(a , b , sizeof(a))
#define rep(i , l , r) for (int i = (l); i <= (r); i ++)
#define per(i , r , l) for (int i = (r); i >= (l); i --)
#define PE(i , x) for(int i = head[x]; i; i = edge[i].last)
#define DEBUG(x) std :: cerr << #x << '=' << x << std :: endl

typedef long long ll;
typedef unsigned long long ull;

template <class T>
inline void read(T &x) {
char c = getchar(), f = 0; x = 0;
while (!isdigit(c)) f = (c == '-'), c = getchar();
while (isdigit(c)) x = x * 10 + c - '0', c = getchar();
x = f ? -x : x;
}

using namespace std;

const int N = 10000 + 5;
const int M = 100000 + 5;
const int HA = 998244353;

int n , a[M];

int main() {
#ifdef LOCAL
freopen("try.in" , "r" , stdin);
freopen("try1.out" , "w" , stdout);
#endif
read(n);
rep(i , 1 , n) read(a[i]);
sort(a + 1, a + n + 1, greater<int>());
rep(i , 1 , n)
if (i + 1 > a[i + 1]) {
int j = 0;
for (; a[j + i + 1] == i; ++j);
if (((a[i] - i) & 1) || (j & 1)) puts("First");
else puts("Second");
break;
}

return 0;
}

THE END