题意:
给出n堆物品,每堆物品都有若干件,玩家A和B进行游戏,每人每轮操作一次,按照如下规则:
- 任意选择一个堆,假设该堆有x个物品,从中选择k个,要保证0<k<x且0<(x xor k)<x。
- 再增加一个大小为x xor k的堆(也就相当于将一个x个物品的堆变成一个k个物品的堆和一个x xor k个物品的堆),另外有一个技能,可以将这个大小为x^k的堆变成(2*k)^x的堆,但是这个技能每个人只有一次机会可以使用。
现在问两人轮流操作,都采取最优策略,最后不能操作的人输,问先手能不能赢。
题解:
答案:假设第i堆有x个物品,cnt为x二进制中1的个数,统计这n堆所有(cnt-1)的和,和为偶数后手赢,奇数先手赢。
解释:
首先我们需要知道:
- 将x个物品的堆变为一个k个物品的堆和一个x xor k个物品的堆,前后二进制中‘1’的个数的奇偶性不变。
- 将x xor k变为 x xor (2*k),前后二进制中‘1’的个数的奇偶性不变。
那么我们先假设只有一堆物品(n=1),假设这堆有9个物品(x=9二进制为1001),那么k只能等于1000或1,x xor k只能等于1或1000,这时我们会发现,当一个数的二进制中‘1’的个数只有一个时,就不能继续分了,则为必败态。
因此,由于所有的操作都不会改变二进制中‘1’个数的奇偶性,那么操作次数的奇偶性一定与所有(cnt-1)的和的奇偶性相同。
而且最终结果一定是分成了许多个二进制中只有一个‘1’的数。
代码:
#include <set> #include <map> #include <cmath> #include <stack> #include <queue> #include <vector> #include <string> #include <cstdio> #include <cstring> #include <sstream> #include <iomanip> #include <iostream> #include <algorithm> #define clr(str,x) memset(str,x,sizeof(str)) #define FRER() freopen("in.txt","r",stdin); #define FREW() freopen("out.txt","w",stdout); #define MAX_INF 0x7fffffff #define INF 0x3f3f3f3f #define maxn 100 using namespace std; int Get(int x) { int cnt=0; while(x>0) { cnt+=x%2; x/=2; } return cnt; } int main() { int T; scanf("%d",&T); for(int Case=1; Case<=T; Case++) { int n,ans=0; scanf("%d",&n); for(int i=0; i<n; i++) { int x; scanf("%d",&x); ans+=Get(x)-1; } if(ans%2==0) printf("Case %d: No\n",Case); else printf("Case %d: Yes\n",Case); } return 0; }