hdu3537

题意:

有个翻硬币游戏,总共有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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值