[CF282D] Yet Another Number Game 题解
[CF282D] Yet Another Number Game
这题可以分三种情况讨论 \(n\) 的取值。
n = 1
显然,当 \(a1 \neq 0\) 时先手可以一下全部取完,后手必败,否则后手必胜。
n = 2
有一个朴素的 DP。
令 \(dp[i][j]\) 表示两堆石头分别剩余 \(i\) 个和 \(j\) 个时先手是否必胜,必胜取 \(1\),否则取 \(0\)。
则 \(dp[i][j] |= !dp[i - k][j](1\leq k\leq i)\)
$ dp[i][j] |= !dp[i][j - k](1\leq k\leq j)$
且有 $ dp[i][j]|= !dp[i - k][j - k](1\leq k\leq \min(i, j))$
分别代表三种可能的取法,这是 \(O(A^3)\) 的,其中 \(A\) 表示 \(a_i\) 的最大取值。
n = 3
可以用前缀和优化上一个转移方程,不过这里用博弈论的方法解决。
结论:若 \(a\oplus b\oplus c \ne 0\) 则先手必胜,否则先手必败,其中 \(\oplus\) 表示按位异或,\(a, b, c\) 代表三堆石子的剩余数量,且令 \(a \le b \le c\)。
构造一种选法:
如果当前局面 \(a\oplus b\oplus c \ne 0\),则先手一定能找到一种操作使得操作后 \(a\oplus b\oplus c = 0\),换言之 \(a\oplus b = c\)。
证明:设 \(a\oplus b = k\),则
若 \(c > k\),选择操作一使 \(c\) 变为 \(k\) 即可
若 \(c < k\),则有 \(a\oplus b > c\),则 \(a>b\oplus c\)。
设 \(b\oplus c = k_2\),此时直接选择操作一使 \(a\) 变为 \(k_2\) 即可
然后到了后手面临的局面为 \(a\oplus b\oplus c = 0\) 时,无论他如何操作都无法使得操作后 \(a\oplus b\oplus c = 0\) 仍然成立。
证明:
对于操作一,操作前 \(a\oplus b = c\),假设操作后 \(a\oplus b = k\),那么 \(c = k\),然而题目要求最少取 \(1\) 颗石子,矛盾。
对于操作二,设 \(a, b, c\) 同时取走了 \(x\) 个,由于 \(a\oplus b = c\),因此 \(a\) 与 \(b\) 和 \(c\) 在二进制下的个位只有四种情况
1 1 0
0 0 0
0 1 1
1 0 1
则若 \(x\) 为奇数时,上述四种情况的末尾必然发生翻转,因此 \(a\oplus b\ne c\)
若 \(x\) 为偶数时,设 \(x\) 末尾有 \(k\) 个 \(0\),就先不看后 \(k\) 位,如果从后往前数第一个 \(1\) 开始看,同 \(x\) 为奇数的情况。
因此重复这种操作,由于 \(a\oplus b\oplus c \ne 0, (a, b, c\leq 0)\),所以必不可能走到形如 0 0 0
的情况,因此先手立于不败之地,而后手不断面临着 \(a\oplus b\oplus c = 0\) 的情况,迟早有一天他会碰到 0 0 0
而输掉游戏。
因此若 \(a\oplus b\oplus c \ne 0\) 则先手必胜,否则先手必败。
代码实现
// Problem: Yet Another Number Game
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/CF282D
// Memory Limit: 250 MB
// Time Limit: 2000 ms
// Author: Moyou
// Copyright (c) 2023 Moyou All rights reserved.
// Date: 2023-02-27 18:56:56
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <iostream>
using namespace std;
const int N = 300 + 10;
int n;
namespace T2
{
bool dp[N][N];
void work()
{
dp[0][0] = 0;
for(int i = 1; i <= N; i ++)
dp[i][0] = dp[0][i] = 1;
for(int i = 1; i <= N; i ++)
{
for(int j = 1; j <= N; j ++)
{
for(int k = 1; k <= i; k ++)
dp[i][j] |= !(dp[i - k][j]);
for(int k = 1; k <= j; k ++)
dp[i][j] |= !(dp[i][j - k]);
for(int k = 1; k <= min(i, j); k ++)
dp[i][j] |= !(dp[i - k][j - k]);
}
}
}
} ;
int main()
{
cin >> n;
int a, b, c;
if (n == 1)
{
cin >> a;
if (a)
cout << "BitLGM" << endl;
else cout << "BitAryo" << endl;
}
else if(n == 2)
{
cin >> a >> b;
T2::work();
cout << (T2::dp[a][b] ? "BitLGM" : "BitAryo") << endl;
}
else
{
cin >> a >> b >> c;
cout << ((a ^ b ^ c) ? "BitLGM" : "BitAryo") << endl;
}
return 0;
}
感谢 chicken_lizard 大佬的指导。