一、巴什游戏
题目简述:有n颗石子,甲先取,乙后取,每次可拿1-m颗石子,轮流拿下去,拿到最后一颗的人获胜。
结论是若n%(m+1) == 0则先手败,否则先手胜。
先想当有m+1个石子时候不论先手取多少个 后手一定可以一次性取完,所以后手一定赢。
那么再思考如果有n*(m+1)个,先手取k个,则后手取m+1-k个保证每一个周期结束后数目仍然是m+1的倍数。
直到剩m+1个的时候,先手取完后,后手全部取完。
如果不是m+1的倍数那么先手只需要一定数目的石子让其变为m+1的倍数即可 所以有当n%(m+1)==0先手必败n%(m+1) != 0先手必胜
二、Nim游戏
P2197 【模板】Nim 游戏 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
尼姆游戏(Nim Game)是由n堆石子,数量分别是{a1,a2,…,an},两个玩家轮流拿石子,每次可以从任意一堆拿走任意数量的石子,拿到最后一个石子的玩家获胜。
结论是若a1⊕a2⊕…an≠0,则先手必胜(N),否则先手必败(P)
三、SG函数
1.mex函数
mex(a1, a2, a3, ... ... , an) 是指 >=0 的数中最小的 没有出现在 a1, a2, a3, ... ... , an 中的数,例如:mex(0, 1, 2, 3) = 4,mex(2, 3) = 0,mex(1, 2) = 0
2.SG函数求解巴什游戏
结论是如果sg(x) = 0,则x点为必败点
关键在计算sg函数的值
void getSG(){
memset(sg, 0, sizeof sg);
for(int i = 0; i <= n; i++){
memset(s, 0, sizeof s);
//把i的后继点放到集合s中
for(int j = 1; j <= m && i - j >= 0; j++){
s[sg[i - j]] = 1;
}
//计算sg[i];
for(int j = 0; j <= n; j++){
if(!s[j]){
sg[i] = j;
break;
}
}
}
}
void solve() {
cin >> n >> m;
getSG();
if(sg[n]) cout << "first\n";
else cout << "second\n";
}
3.SG函数求解Nim游戏
结论是计算每一堆石子的sg函数值,再全都异或起来
一般会在取石子个数有特殊规则时使用。
Not a Nim Problem - Codeforces
#include<bits/stdc++.h>
using namespace std;
#define qio ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
typedef long long ll;
typedef double db;
const int N = 3e5 + 10;
const int M = 1e7 + 10;
int a[N], sg[M];
void init(){
sg[1] = 1;
sg[2] = 0;
int cnt = 1;
for(int i = 3; i <= M; i++){
if(sg[i] || i % 2 == 0) continue;
cnt++;
sg[i] = cnt;
for(int j = 1; i * j <= M; j++){
if(sg[i * j] == 0 && (i * j) % 2 == 1) sg[i * j] = cnt;
}
}
}
void solve() {
int n; cin >> n;
for(int i = 1; i <= n; i++) cin >> a[i];
int ans = 0;
for(int i = 1; i <= n; i++){
ans ^= sg[a[i]];
}
if(ans){
cout << "Alice\n";
}else{
cout << "Bob\n";
}
}
signed main() {
qio
int T = 1;
init();
cin >> T;
while (T--)solve();
}