G - Being a Good Boy in Spring Festival
Time Limit:1000MS Memory Limit:32768KB 64bit IO Format:%I64d & %I64u
Appoint description:
Description
一年在外 父母时刻牵挂
春节回家 你能做几天好孩子吗
寒假里尝试做做下面的事情吧
陪妈妈逛一次菜场
悄悄给爸爸买个小礼物
主动地 强烈地 要求洗一次碗
某一天早起 给爸妈用心地做回早餐
如果愿意 你还可以和爸妈说
咱们玩个小游戏吧 ACM课上学的呢~
下面是一个二人小游戏:桌子上有M堆扑克牌;每堆牌的数量分别为Ni(i=1…M);两人轮流进行;每走一步可以任意选择一堆并取走其中的任意张牌;桌子上的扑克全部取光,则游戏结束;最后一次取牌的人为胜者。
现在我们不想研究到底先手为胜还是为负,我只想问大家:
――“先手的人如果想赢,第一步有几种选择呢?”
春节回家 你能做几天好孩子吗
寒假里尝试做做下面的事情吧
陪妈妈逛一次菜场
悄悄给爸爸买个小礼物
主动地 强烈地 要求洗一次碗
某一天早起 给爸妈用心地做回早餐
如果愿意 你还可以和爸妈说
咱们玩个小游戏吧 ACM课上学的呢~
下面是一个二人小游戏:桌子上有M堆扑克牌;每堆牌的数量分别为Ni(i=1…M);两人轮流进行;每走一步可以任意选择一堆并取走其中的任意张牌;桌子上的扑克全部取光,则游戏结束;最后一次取牌的人为胜者。
现在我们不想研究到底先手为胜还是为负,我只想问大家:
――“先手的人如果想赢,第一步有几种选择呢?”
Input
输入数据包含多个测试用例,每个测试用例占2行,首先一行包含一个整数M(1<M<=100),表示扑克牌的堆数,紧接着一行包含M个整数Ni(1<=Ni<=1000000,i=1…M),分别表示M堆扑克的数量。M为0则表示输入数据的结束。
Output
如果先手的人能赢,请输出他第一步可行的方案数,否则请输出0,每个实例的输出占一行。
Sample Input
3 5 7 9 0
Sample Output
1
nim博弈题,设s(N)=N0^N1^N2^...^Nm
1.先把所有的异或一遍(s(N)),如果是零,则先手的人输
2.题目难点是非零时如何求第一步的可行方案
思路:我们要保证赢,则要从其中一堆拿出一定量的扑克牌后,要使剩下的所有堆异或起来等于零,也就是让对方面对必输局面
我们可以把所有的扑克牌堆分为两部分,一部分B1只有一堆(也就是你要拿的那一堆,拿完之后为B1'),剩下的为另一部分B2,要使剩下的所有堆异或起来等于零,也就是s(B1’)^s(B2)==0,s(B2)易求,接下来是如何确定能不能找到这个B1’和能找到多少个B1’
两个数a,b 如果要使0~a-1中的一个数异或上b等于零,那么a肯定是大于b的,因为一个数异或上他本身才为零,并且0~a-1中只有一个数满足异或上b为零(也就是b);
所以只有当s(B1)大于s(B2)时,才有一个B1’,使s(B1’)^s(B2)==0,s(B1)也就是B1这个堆牌的数量,每个堆都遍历一遍即可求出所有的可行方案;
#include<stdio.h>
int a[200],n;
int main()
{
int i,j,cnt=0,sum=0,cnt2;
while(~scanf("%d",&n),n)
{
sum=0;
cnt=0;
for(i=0;i<n;i++)
{
scanf("%d",&a[i]);
cnt^=a[i];
}
if(cnt==0)
printf("0\n");
else
{
for(i=0;i<n;i++)
{
cnt2=cnt^a[i];//cnt是所有数的异或起来,在异或一遍a[i]则为除去a[i]剩下的数异或起来的结果(也就是s(B2)在网上看到,比较高效)
// printf("%d\n",cnt);
if(cnt2<a[i])
sum++;
}
printf("%d\n",sum);
}
}
return 0;
}