HDU_1850_nim游戏

Being a Good Boy in Spring Festival

Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 6842    Accepted Submission(s): 4144


Problem Description
一年在外 父母时刻牵挂
春节回家 你能做几天好孩子吗
寒假里尝试做做下面的事情吧

陪妈妈逛一次菜场
悄悄给爸爸买个小礼物
主动地 强烈地 要求洗一次碗
某一天早起 给爸妈用心地做回早餐

如果愿意 你还可以和爸妈说
咱们玩个小游戏吧 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
 

[定理1]:对于任何一个S态,总能从一堆火柴中取出若干个使之成为T态。

证明:

    若有n堆火柴,每堆火柴有A(i)根火柴数,那么既然现在处于S态,

    c = A(1) xor A(2) xor … xor A(n) > 0;

  把c表示成二进制,记它的二进制数的最高位为第p位,则必然存在一个A(t),它二进制的第p位也是1。(否则,若所有的A(i)的第p位都是0,这与c的第p位就也为0矛盾)。

    那么我们把x = A(t) xor c,则得到x < A(t).这是因为既然A(t)的第p位与c的第p位同为1,那么x的第p位变为0,而高于p的位并没有改变。所以x < A(t).而

        A(1) xor A(2) xor … xor x xor … xor A(n)

     = A(1) xor A(2) xor … xor A(t) xor c xor … xor A(n)

     = A(1) xor A(2) xor… xor A(n) xor A(1) xor A(2) xor … xor A(n)

   = 0

  这就是说从A(t)堆中取出 A(t) - x 根火柴后状态就会从S态变为T态。证毕

•对于某个局面(a1,a2,...,an),若a1^a2^...^an==k(k>0)
•一定存在某个合法的移动,将ai改变成ai'后满足 a1^a2^...^ai'^...^an=0
•一定存在某个ai,它的二进制表示在k的最高位上是1 (ai^k<ai 成立)
•将ai改变成ai'=ai^k a1^a2^...^ai'^...^an=a1^a2^...^an^k=0
 有了这个,我们就可以直接找含有最高位为1的值就可以了,但是一定要注意,找到最高位为1后还要判断ai^k<ai,因为有可能有K值的最高位并不是在ai中,譬如k=1,10^K>10.
 
#include<iostream>
#include<cstdio>
#include<cstring>
#include<stdlib.h>
#include<algorithm>
#include<cmath>
using namespace std;

int N[105];
int main()
{
    int m;
    while(scanf("%d",&m)!=EOF&&m)
    {
        int k=0;
        for(int i=0; i<m; i++)
        {
            scanf("%d",&N[i]);
            k^=N[i];
        }
        if(k==0)
            printf("0\n");
        else
        {
            int pos=0;
            for(int i=0;i<=30;i++)
            {
                int tmp=(1<<i);
                if((tmp&k)>0)
                    pos=i;
            }
            //cout<<pos<<endl;
            int res=0;
            for(int i=0;i<m;i++)
                if((N[i]&(1<<pos))>0&&(N[i]^k)<N[i])
                    res++;
            printf("%d\n",res);
        }
    }
    return 0;
}

 

posted on 2016-09-23 12:13  JASONlee3  阅读(416)  评论(0编辑  收藏  举报

导航