[AGC002E] Candy Piles - 博弈论

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

1.将当前最大的那堆糖果全部吃完
2.将每堆糖果吃掉一个

吃完的人输,假设两人足够聪明,问谁能必胜 1<=n<=10^5 1<=ai<=10^9
输出 First(表示第一个人必胜),或 Second(表示第二个人必胜

思路

可以把糖果降序排序,转换成 n 列网格,每列的高度为每堆的数量,如下图所示
[AGC002E] Candy Piles - 博弈论_第1张图片
起点从 (1,1) 开始,操作 1 可转换为向右走一格,操作 2 可转换为向上走一格,当走到边界时即为吃完所有糖果

所以可以得到边界的点都为必败点,若一个点上面或右边有必胜点这个点一定为必败点(对手一定会走到必胜点),否则为必胜
[AGC002E] Candy Piles - 博弈论_第2张图片
若 (1,1) 为必败点则先手必胜(可看为后手操作到了 (1,1),所以 (1,1) 是后手的胜负状态),反之亦然

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

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

代码

/************************************************
*Author        : www.jintianxuesha.com lrj124
*Created Time  :  www.chuancenpt.com 2020.08.14.11:48
*Mail          :   www.yixingylzc.cn [email protected]
*Problem       :  at1999
************************************************/
#include 
#include 
#include 
using namespace std;
const int maxn = 1e5 + 10;
int n,a[maxn];
int main(www.hongtuupt.cn) {
	freopen("at1999.in","r",stdin);
	freopen("at1999.out","w",stdout);
	scanf(www.xinhuihpw.com"%d",&n);
	for (int i = 1;i <= n;i++) scanf("%d",&a[i]);
	sort(a+1,a+n+1,greater());
	for (int i = 1;i <= n;i++)
		if (i www.guanghuidq.cn>= a[i+1]) {
			int cnt = 0;
			for (int j = i+1;j <= n;j++) if (a[j] == i) cnt++; else break;
			return puts((a[www.fanhuagjqw.cn]-i)&1 || cnt&1 ? "First" : "Second"),0;
		}
	return 0;

你可能感兴趣的:([AGC002E] Candy Piles - 博弈论)