【AGC002E】 Candy Piles - 博弈论

题目大意

桌上有 N 堆糖果,第 i 堆糖果有Ai 个糖。两人在玩游戏,轮流进行,每次进行下列两个操作中的一个

  1. 将当前最大的那堆糖果全部吃完

  2. 将每堆糖果吃掉一个

吃完的人输,假设两人足够聪明,问谁能必胜 1<=n<=10^5 1<=ai<=10^9

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

思路

可以把糖果降序排序,转换成 n 列网格,每列的高度为每堆的数量,如下图所示

起点从 (1,1) 开始,操作 1 可转换为向右走一格,操作 2 可转换为向上走一格,当走到边界时即为吃完所有糖果

所以可以得到边界的点都为必败点,若一个点上面或右边有必胜点这个点一定为必败点(对手一定会走到必胜点),否则为必胜

若 (1,1) 为必败点则先手必胜(可看为后手操作到了 (1,1),所以 (1,1) 是后手的胜负状态),反之亦然

然后仔细观察发现同一条对角线上的点胜负状态一定相同,所以可以计算对角线上最后一个点的胜负来判断起点的胜负

然后找规律,若对角线上最后一个点到最上面或到最右边的距离有奇数,这个点即为必败点

/************************************************
*Author        :  lrj124
*Created Time  :  2020.08.14.11:48
*Mail          :  1584634848@qq.com
*Problem       :  at1999
************************************************/
#include <algorithm>
#include <iostream>
#include <cstdio>
using namespace std;
const int maxn = 1e5 + 10;
int n,a[maxn];
int main() {
//	freopen("at1999.in","r",stdin);
//	freopen("at1999.out","w",stdout);
	scanf("%d",&n);
	for (int i = 1;i <= n;i++) scanf("%d",&a[i]);
	sort(a+1,a+n+1,greater<int>());
	for (int i = 1;i <= n;i++)
		if (i >= a[i+1]) {
			int cnt = 0;
			for (int j = i+1;j <= n;j++) if (a[j] == i) cnt++; else break;
			return puts((a[i]-i)&1 || cnt&1 ? "First" : "Second"),0;
		}
	return 0;
}
posted @ 2020-08-15 16:16  lrj124  阅读(236)  评论(0编辑  收藏  举报