JS1k: Breathing Galaxies (1013 bytes)

博弈论

博弈论基础

Nim 游戏

地上有 \(n\) 堆石子(每堆石子数量小于 \(10^4\)),每人每次可从任意一堆石子里取出任意多枚石子扔掉,可以取完,不能不取。每次只能从一堆里取。最后没石子可取的人就输了。假如甲是先手,且告诉你这 \(n\) 堆石子的数量,是否存在先手必胜的策略。

把每个状态转化为有向无环图上的一个节点,向它的后继状态连边,可以得到一个博弈图。

\(\text{SG}\) 函数

定义 \(\text{mex}(S)\) 为不属于集合 \(S\) 的最小非负整数,即

\[\text{mex}(S)=min\{x\}(x\notin S,x\in N) \]

对于状态 \(x\) 和它的所有 \(k\) 个后继状态 \(y_1,y_2,\ldots,y_k\),定义 \(\text{SG}\) 函数:

\[\text{SG}(x)=\text{mex}\{\text{SG}(y_1),\text{SG}(y_2),\ldots,\text{SG}(y_k)\} \]

\(\text{SG}\) 定理

对于由 \(n\) 个有向图游戏组成的组合游戏,设它们的起点分别为 \(s_1,s_2,\ldots,s_n\),则有:当且仅当 \(\text{SG}(s_1)\oplus\text{SG}(s_2)\oplus\ldots\oplus\text{SG}(s_n)\neq0\) 时,这个游戏是先手必胜的。同时,这是这一个组合游戏的游戏状态 \(x\)\(\text{SG}\) 函数值。

证明较为复杂,详情可以查看 oi-wiki


回到题目,对于每堆石子可以转化为一个普通的取石子游戏。

于是可以得到大小为 \(n\) 的石子的 \(\text{SG}\) 函数值为 \(n\),所以只需要判断石子数量的异或和即可。

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e4 + 5;
int T, n, x;
signed main(){
	cin.tie(0)->sync_with_stdio(0);
	cin >> T;
	while (T--){
		cin >> n;
		int ans = 0;
		for (int i = 1; i <= n; ++i){
			cin >> x;
			ans ^= x;
		}
		cout << (ans ? "Yes" : "No") << "\n";
	}
	return 0;
}

阶梯取石子游戏

\(n\) 堆石头,依次放在阶梯上,甲和乙轮流操作。每次从任意一堆取出至少一个石头,放到下一个阶梯,如果在第 \(1\) 层则将石头扔掉,不能取的输。问谁会获胜。\(n\le10^5\)

类似地,只需保留奇数层的石子,偶数层的石子看作被扔掉,因为一个人将其移动到
奇数层后,另一个人可以又将其移动到偶数层。

于是与普通 Nim 游戏相同。

Bash 博弈

\(n\) 堆石头,甲和乙轮流取。每次从任意一堆取出至少一个石头,至多 \(m\) 个,不能取的输。问谁会获胜。\(n\le10^5\)

(以下叙述均为对于一堆石子)首先 \(\text{SG}(0)=0\),因为若当前石子数为 \(0\),则先手必败。于是可以得到 \(\text{SG}(1)=\text{SG}(2)=\ldots=\text{SG}(m)=1\),因为 \(1\sim m\) 的状态都可以转化为 \(0\) 的状态,是对手面临 \(0\) 的状态,所以这些状态都为先手必胜。于是又可以得到 \(\text{SG}(m+1)=0\),因为 \(m+1\) 的状态只能转移为 \(1\sim m\) 的状态,让对手面临先手必胜状态,此时,己方必败。后续 \(\text{SG}\) 都可以由定义得到:\(\text{SG}(m+2)=2\)……

可以发现,大小为 \(n\) 的石子堆的 \(\text{SG}\) 函数值为 \(n\bmod(m+1)\),可以转化为普通 Nim 游戏进行求解。

Game on Tree

有一棵 \(N\) 个节点的树,节点标号为 \(1,2,⋯,N\),边用 \((x_i,y_i)\) 表示。Alice 和 Bob 在这棵树上玩一个游戏,Alice 先手,两人轮流操作:

选择一条树上存在的边,把它断开使树变成两个连通块。然后把不包含 \(1\) 号点的联通块删除

当一个玩家不能操作时输,你需要算出:假如两人都按最优策略操作,谁将获胜。

对于树上的节点 \(u\) 计算其子树游戏的 \(\text{SG}\) 函数,于是可以得到 \(\text{SG}(u)\) 为其所有子节点的 \(SG\) 函数值加 \(1\)(加一条边),叶子节点的 \(\text{SG}\) 函数值为 \(0\),于是转化为普通 Nim 游戏求解。

反 Nim(Anti-Nim)游戏

同普通取石子游戏规则,但是不能操作的人胜利。

\(T=\oplus_{i=1}^{n}a_{i}\)

结论:

  1. 当全部 \(a_i=1\),如果有奇数堆石子则先手必败,有偶数堆则先手必胜。
  2. 当至少一个 \(a_{i}>1\)\(T\neq 0\) 时则先手必胜,否则先手必败。

证明可见 oi-wiki

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 55;
int T, n;
signed main(){
	cin.tie(0)->sync_with_stdio(0);
	cin >> T;
	while (T--){
		cin >> n;
		int flag = 0, flag2 = 0, x, ans = 0;
		for (int i = 1; i <= n; ++i){
			cin >> x;
			if (x > 1)flag = 1;
			if (x != 1)flag2 = 1;
			ans ^= x;
		}
		if (!flag2 && (n & 1))cout << "Brother\n";
		else if (!flag2 && !(n & 1))cout << "John\n";
		else {
			if (flag && ans)cout << "John\n";
			else cout << "Brother\n";
		}
	}
	return 0;
} 

参考资料

oi-wikioi-wiki

posted @ 2025-08-02 09:52  __int127  阅读(16)  评论(1)    收藏  举报