题意:
有个翻硬币游戏,总共有10^8个硬币排成一列,它们从0开始编号。现在有n个硬币是正面朝上的,每次可以选择一个大小不超过3的硬币集合,将它们翻转,要求选择集合的最右边的那个硬币一定是正面朝上的,以及不能不选硬币。当一个人无法操作时算输。给出初始哪些硬币是正面朝上的,问先手是否必胜
solution:
对于翻硬币游戏,有个定理,当前游戏的sg值等价于所有正面向上硬币单独存在时的sg值的xor和。
因为翻硬币有最右端必须正面朝上的规定,那么每个硬币就可以看做一个独立的游戏。如果选择的左边的硬币本来就是正面朝上的,那么这个决策又将后继状态的sg值xor过来,就相当于抵消了。
那么打张表,记编号为x的硬币单独存在时的sg值为sg[x],有sg[x] = 2x 或 2x+1
不难发现,将x二进制转换,若二进制数码中1的个数为偶数,需要加一
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<bitset>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;
int n,sum;
map <int,bool> M;
int getint()
{
char ch = getchar(); int ret = 0;
while (ch < '0' || '9' < ch)
{
if (ch == EOF) return -1;
ch = getchar();
}
while ('0' <= ch && ch <= '9')
ret = ret * 10 + ch - '0',ch = getchar();
return ret;
}
int Calc(int k)
{
int ret = (k << 1),cnt = 0;
while (k) cnt += (k & 1),k >>= 1;
return ret + (cnt & 1 ? 0 : 1);
}
int main()
{
#ifdef DMC
freopen("DMC.txt","r",stdin);
#endif
for (;;)
{
n = getint(); if (n == -1) break;
while (n--)
{
int x = getint();
if (M.count(x)) continue;
sum ^= Calc(x); M[x] = 1;
}
puts(sum ? "No" : "Yes");
M.clear(); sum = 0;
}
return 0;
}