Nim游戏

题目

\(N\) 堆石子,两人轮流从其中一堆取至少一个石子,问先手是否存在必胜策略。

结论

异或不为0,先手必胜。

证明

\(k\) 为某一堆取完后的剩余个数,\(i\) 为被取那堆石子的编号,则取完后的异或和为 \(x_1\;xor\;x_2\;xor\dots xor\;x_{i-1}\;xor\;x_{i+1}\;xor\dots xor\;x_n\;xor\; k\)

情况一:异或和为0

  1. 总数为0,此时先手输了
  2. 总数不为0,此时先手还要使异或和为0,就必须使得 \(k=x_i\),然而由于至少取一个,所以这是不可能的,所以必然会转为情况二。

情况二:异或和不为0

可以证明,必然可以取为0。

\(S=x_1\;xor\;x_2\;xor\dots xor\;x_n\)\(x_i\)\(x\) 中的最大值。

  1. \(S\) 的二进制位数小于 \(x_i\) 的位数,则 \(x_i\) 必然可以凑出一个数使得其异或 \(S\) 等于0。
  2. \(S\) 的二进制位数和 \(x_i\) 的二进制位数相同,需满足 \(k\leqslant x_i\)\(S\;xor\; x_i\;xor\;k=0\)。由于 \(S\;xor\; x_i\) 时已经使得二进制最高位为0,所以 \(k\) 的二进制最高位必然为0。\(k\) 在二进制的最高位已经满足小于 \(x_i\),后面的位数可以随便取,那么只需要取 \(S\;xor\; x_i\) 即可(这个数的二进制最高位也为0)。

代码

#include<bits/stdc++.h>
using namespace std; 
int n, m, i, j, k, T; 

int main()
{
	scanf("%d", &n); 
	for(i=1; i<=n; ++i) scanf("%d", &k), m^=k; 
	printf(m ? "win" : "lose"); 
	return 0; 
}
posted @ 2022-03-14 17:21  zhangtingxi  阅读(87)  评论(0编辑  收藏  举报