以 【模板】nim 游戏 和 【一本通提高博弈论】S-Nim为例。
Nim游戏
结论
如果
a 1 ⊕ a 2 ⊕ a 3 ⊕ … ⊕ a n ≠ 0 a_{1}\oplus a_{2} \oplus a_{3}\oplus … \oplus a_{n}≠0 a1⊕a2⊕a3⊕…⊕an=0
那么先手必胜。否则,先手必败。
证明
-
我们考虑,如果在操作结束,什么也不能取的情况下,即:
0 ⊕ 0 ⊕ 0 ⊕ … ⊕ 0 = 0 0\oplus 0 \oplus 0\oplus … \oplus 0 = 0 0⊕0⊕0⊕…⊕0=0
如果最后结果为 0 0 0 也就代表失败了。
-
如果在操作中,当前为 a 1 ⊕ a 2 ⊕ a 3 ⊕ … ⊕ a n ≠ 0 = x a_{1}\oplus a_{2} \oplus a_{3}\oplus … \oplus a_{n}≠0=x a1⊕a2⊕a3⊕…⊕an=0=x,那么一定有一种方法使原式等于 0 0 0。
根据异或的性质,二进制第 k k k 为不同为 1 1 1,则 x x x 的最高位的 1 1 1 若在第 k k k 位上,一定有至少有一个 a i a_{i} ai 的第 k k k 位为 1 1 1。
这时, a i ⊕ x ≤ a i a_{i}\oplus x \le a_{i} ai⊕x≤ai,可以从 a i a_{i} ai 中取出 a i − ( a i ⊕ x ) a_{i}-(a_{i}\oplus x) ai−(ai⊕x) 个石子(根据前文石子个数必大于 0 0 0),那么此时堆中石子个数就为 a i − [ a i − ( a i ⊕ x ) ] = a i ⊕ x a_{i}-[a_{i}-(a_{i}\oplus x)]=a_{i}\oplus x ai−[ai−(ai⊕x)]=ai⊕x,将其带入原式进行操作,即:
a 1 ⊕ a 2 ⊕ a 3 ⊕ … ⊕ a i ⊕ x ⊕ … ⊕ a n = x ⊕ x a_{1}\oplus a_{2} \oplus a_{3}\oplus …\oplus a_{i} \oplus x\oplus… \oplus a_{n}=x\oplus x a1⊕a2⊕a3⊕…⊕ai⊕x⊕…⊕an=x⊕x
我们就可以得出,总有一种方法,是当前局面从不等于 0 0 0 变为 0 0 0。
-
如果在操作中,当前为 a 1 ⊕ a 2 ⊕ a 3 ⊕ … ⊕ a n = 0 a_{1}\oplus a_{2} \oplus a_{3}\oplus … \oplus a_{n}=0 a1⊕a2⊕a3⊕…⊕an=0,那么无论进行任何操作,都不能使原式等于 0 0 0。
考虑反证:
设 a i ′ a_{i}' ai′ 为从任意一堆 a i a_{i} ai 中取出若干个石子后的个数,满足 0 ≤ a i ′ ≤ a i 0\le a_{i}'\le a_{i} 0≤ai′≤ai。
存在
a 1 ⊕ a 2 ⊕ a 3 ⊕ … ⊕ a i ⊕ … ⊕ a n = 0 a_{1}\oplus a_{2} \oplus a_{3}\oplus …\oplus a_{i} \oplus … \oplus a_{n}=0 a1⊕a2⊕a3⊕…⊕ai⊕…⊕an=0
a 1 ⊕ a 2 ⊕ a 3 ⊕ … ⊕ a i ′ ⊕ … ⊕ a n = 0 a_{1}\oplus a_{2} \oplus a_{3}\oplus …\oplus a_{i}' \oplus … \oplus a_{n}=0 a1⊕a2⊕a3⊕…⊕ai′⊕…⊕an=0
将两式相互异或,得
a i ⊕ a i ′ = 0 a_{i}\oplus a_{i}'=0 ai⊕ai′=0
因为 0 ≤ a i ′ < a i 0\le a_{i}'< a_{i} 0≤ai′<ai,所以此等式不成立。则无论进行任何操作,都不能使原式等于 0 0 0。
当还是初始状态时,如果不等于 0 0 0,先手一定可以将一个等于 0 0 0 的式子推给对方;而后手因为无论如何操作都不能使其等于 0 0 0,所以必会将一个不等于 0 0 0 的式子推回来。先手必胜。
如果等于 0 0 0,那先手无论如何操作都不能使其等于 0 0 0,所以必会将一个不等于 0 0 0 的式子推给对方;而后手这时就可以将等于 0 0 0 的式子推给我们。先手必败。
贴代码
#include<bits/stdc++.h>
using namespace std;
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n;
scanf("%d",&n);
int sum=0;
for(int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
sum^=x;
}
if(!sum) puts("No");
else puts("Yes");
}
return 0;
}
S-Nim
SG函数与mex运算
-
m e x mex mex 运算是将一个不存在与集合中的最小非负整数。
-
我们可以将石子的数量变化转化成一张有向图的形式, S G SG SG 函数就是将 k k k 个状态 y i y_{i} yi 的 S G SG SG 函数值取 m e x mex mex,即:
m e x ( { S G ( y 1 ) , S G ( y 2 ) , S G ( y 3 ) … S G ( y k ) } ) mex(\left \{ SG(y_{1}),SG(y_{2}),SG(y_{3})…SG(y_{k})\right \} ) mex({SG(y1),SG(y2),SG(y3)…SG(yk)})
对于终点来说,因为无法再变化,所以为空集, S G SG SG 函数值即为 0 0 0。
结论
如果
S G ( a 1 ) ⊕ S G ( a 2 ) ⊕ S G ( a 3 ) ⊕ … ⊕ S G ( a n ) ≠ 0 SG(a_{1})\oplus SG(a_{2}) \oplus SG(a_{3})\oplus … \oplus SG(a_{n})≠0 SG(a1)⊕SG(a2)⊕SG(a3)⊕…⊕SG(an)=0
那么先手必胜。否则,先手必败。
证明
-
如果在操作中,当前为 S G ( a 1 ) ⊕ S G ( a 2 ) ⊕ S G ( a 3 ) ⊕ … ⊕ S G ( a n ) ≠ 0 = x SG(a_{1})\oplus SG(a_{2}) \oplus SG(a_{3})\oplus … \oplus SG(a_{n})≠0=x SG(a1)⊕SG(a2)⊕SG(a3)⊕…⊕SG(an)=0=x,那么一定有一种方法使原式等于 0 0 0。
根据异或的性质,两数二进制第 k k k 为不同为 1 1 1,则 x x x 的最高位的 1 1 1 若在第 k k k 位上,一定有至少有一个 S G ( a i ) SG(a_{i}) SG(ai) 的第 k k k 位为 1 1 1。
这时, S G ( a i ) ⊕ x ≤ S G ( a i ) SG(a_{i})\oplus x \le SG(a_{i}) SG(ai)⊕x≤SG(ai),根据 S G SG SG 函数定义,可以取到 S G ( a i ) ⊕ x SG(a_{i})\oplus x SG(ai)⊕x。可以从 a i a_{i} ai 中取出 S G ( a i ) − ( S G ( a i ) ⊕ x ) SG(a_{i})-(SG(a_{i})\oplus x) SG(ai)−(SG(ai)⊕x) 个石子(根据前文石子个数必大于 0 0 0),那么此时堆中石子个数就为 S G ( a i ) − [ S G ( a i ) − ( S G ( a i ) ⊕ x ) ] = a i ⊕ x SG(a_{i})-[SG(a_{i})-(SG(a_{i})\oplus x)]=a_{i}\oplus x SG(ai)−[SG(ai)−(SG(ai)⊕x)]=ai⊕x,将其带入原式进行操作,即:
S G ( a 1 ) ⊕ S G ( a 2 ) ⊕ S G ( a 3 ) ⊕ … ⊕ S G ( a i ) ⊕ x ⊕ … ⊕ S G ( a n ) = x ⊕ x SG(a_{1})\oplus SG(a_{2}) \oplus SG(a_{3})\oplus …\oplus SG(a_{i})\oplus x\oplus… \oplus SG(a_{n})=x\oplus x SG(a1)⊕SG(a2)⊕SG(a3)⊕…⊕SG(ai)⊕x⊕…⊕SG(an)=x⊕x
我们就可以得出,总有一种方法,是当前局面从不等于 0 0 0 变为 0 0 0。
-
如果在操作中,当前为 S G ( a 1 ) ⊕ S G ( a 2 ) ⊕ S G ( a 3 ) ⊕ … ⊕ S G ( a n ) = 0 SG(a_{1})\oplus SG(a_{2}) \oplus SG(a_{3})\oplus … \oplus SG(a_{n})=0 SG(a1)⊕SG(a2)⊕SG(a3)⊕…⊕SG(an)=0,那么无论进行任何操作,都不能使原式等于 0 0 0。
考虑反证:
设 a i ′ a_{i}' ai′ 为从任意一堆 a i a_{i} ai 中取出若干个石子后的个数。
设 S G ( a i ) SG(a_{i}) SG(ai) 为 k k k,如果 S G ( a 1 ) ⊕ S G ( a 2 ) ⊕ S G ( a 3 ) ⊕ … ⊕ S G ( a i ′ ) ⊕ … ⊕ S G ( a n ) = 0 SG(a_{1})\oplus SG(a_{2}) \oplus SG(a_{3})\oplus …\oplus SG(a_{i}')\oplus… \oplus SG(a_{n})=0 SG(a1)⊕SG(a2)⊕SG(a3)⊕…⊕SG(ai′)⊕…⊕SG(an)=0,那么 S G ( a i ′ ) SG(a_{i}') SG(ai′) 仍然为 k k k,根据 S G SG SG 函数的基本性质, S G ( a i ′ ) SG(a_{i}') SG(ai′),取不到 k k k,所以等式不成立,则无论进行任何操作,都不能使原式等于 0 0 0。
当还是初始状态时,如果不等于 0 0 0,先手一定可以将一个等于 0 0 0 的式子推给对方;而后手因为无论如何操作都不能使其等于 0 0 0,所以必会将一个不等于 0 0 0 的式子推回来。先手必胜。
如果等于 0 0 0,那先手无论如何操作都不能使其等于 0 0 0,所以必会将一个不等于 0 0 0 的式子推给对方;而后手这时就可以将等于 0 0 0 的式子推给我们。先手必败。
贴代码
#include<bits/stdc++.h>
using namespace std;
int s[101];
int SG[10001];
bool vis[10001];
int k,n,m;
void SG_init()
{
for(int i=1;i<=10001;i++)
{
memset(vis,0,sizeof(vis));
for(int j=1;j<=k;j++)
{
if(i<s[j]) break;
vis[SG[i-s[j]]]=1;
}
int cnt=0;
while(vis[cnt])
cnt++;
SG[i]=cnt;
}
return ;
}
int main()
{
while(scanf("%d",&k)&&k)
{
for(int i=1;i<=k;i++) scanf("%d",&s[i]);
sort(s+1,s+k+1);
SG_init();
scanf("%d",&m);
while(m--)
{
int sum=0;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
sum^=SG[x];
}
if(sum) putchar('W');
else putchar('L');
}
puts("");
}
return 0;
}