数论练习2:M - Being a Good Boy in Spring Festival (SG函数融汇贯通)

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)初步理解:这题有了一题的理解这题还有PPT的讲解,知道第一步是什么求出来的了……

(2)加深理解:这也就是说对于一个必胜态,我们的目的是要把必胜状态转化为必败状态从而使得先手胜利。若a1^a2^...^an!=0,一定存在某个合法的移动,将ai改变成ai‘后满足a1^a2^...^ai’^...^an=0。若a1^a2^...^an=sg,则一定存在某个ai,有ai^sg<ai一定成立。则我们可以将ai改变成ai=ai^sg,此时a1^a2^...^ai’^...^an = a1^a2^...^an^sg=0。(这里的判断就是采用了异或的性质)……这两句话从PPT中来的,我感觉说得有点让人晕……我还说得清楚一点吧!

(3)几乎理解:因为由三堆石头举例再拓展而来:(a,b,c)=sg,即:a^b^c=sg,那么必有:(a^b^c)^sg=0,这不就转换为奇异局势了嘛!(因为两个相同的数二进制是一样的,所以异或为0),而(a^b^c)^sg=0可以写成(a^sg)^b^c=0、a^(b^sg)^c=0、a^b^(c^sg)=0,即把其中一个数变为ai=ai^sg,其结果都可以变成奇异局势,那么就可以理解好多堆石头的情况了……

(4)全部理解:但是理解了这个,新的难理解的问题又来了!!!因为可以找到一个数ai^sg然后所以的数异或后为0才可以判断其符合为赢,但是为什么PPT中讲解的判断是只要:a[i]>sg^a[i]就可以判断了呢,我想了一会才发现因为ai^sg是唯一的值,刚开始我理解错了,以为ai以内可能存在多个值使得先手赢呢。因为先手不管取多少都会使ai变小,所以只要ai^sg小于ai就可以判断符合了!

#include<iostream>
#include<map>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<stack>
#include<queue>
#include<set>
using namespace std;
int main()
{
    int t;
    while(cin>>t&&t)
    {
        int a[105],i,sg=0,sum=0;
        for(i=0;i<t;i++)
        {
            cin>>a[i];
            sg^=a[i];
        }
        if(sg==0) {cout<<0<<endl;continue;}
        for(i=0;i<t;i++)
        {
            int s=sg^a[i];
            if(a[i]>s) sum++;
        }
        cout<<sum<<endl;
    }
    return 0;
}


你可能感兴趣的:(数论练习2:M - Being a Good Boy in Spring Festival (SG函数融汇贯通))