hdu 3032 Nim or not Nim?

来源:互联网 发布:淘宝盗图怎么修改 编辑:程序博客网 时间:2024/06/03 19:05

题目链接:点这里

Problem Description

Nim is a two-player mathematic game of strategy in which players take turns removing objects from distinct heaps. On each turn, a player must remove at least one object, and may remove any number of objects provided they all come from the same heap.Nim is usually played as a misere game, in which the player to take the last object loses. Nim can also be played as a normal play game, which means that the person who makes the last move (i.e., who takes the last object) wins. This is called normal play because most games follow this convention, even though Nim usually does not.Alice and Bob is tired of playing Nim under the standard rule, so they make a difference by also allowing the player to separate one of the heaps into two smaller ones. That is, each turn the player may either remove any number of objects from a heap or separate a heap into two smaller ones, and the one who takes the last object wins.

Input

Input contains multiple test cases. The first line is an integer 1 ≤ T ≤ 100, the number of test cases. Each case begins with an integer N, indicating the number of the heaps, the next line contains N integers s[0], s[1], ...., s[N-1], representing heaps with s[0], s[1], ..., s[N-1] objects respectively.(1 ≤ N ≤ 10^6, 1 ≤ S[i] ≤ 2^31 - 1)

Output

For each test case, output a line which contains either "Alice" or "Bob", which is the winner of this game. Alice will play first. You may asume they never make mistakes.

Sample Input

232 2 323 3

Sample Output

AliceBob

【题意】

有很多堆石子,每堆石子告诉你数量,然后两个人一起游戏,对于每个人,你可以取走一堆石子里面的任意数量,或者将一堆超过1个的石子堆分成两个都不为空的石子堆。最后不能移动的人输,现在告诉你Alice先移动,问你最后谁能胜利(假设两个人都完美移动)。

【分析】

博弈问题嘛,反正就是推导难,实现起来毫无难度,谁都写的出来但是就是谁都推导不出来。下面说一些推导过程:

首先有:

SG[0]=0;SG[1]=1;

对于2有:

2的后继状态有:0,1,(1,1),对应的SG有0,1,0,所以SG[2]=2。

对于3有:

3的后继状态有:0,1,2,(1,2),对应的SG有0,1,2,3,所以SG[3]=4。

对于4有:

4的后继状态有:0,1,2,3,(1,3),(2,2),对应的SG有0,1,2,4,5,0,所以SG[4]=3。

然后我们就可以推导出:

SG[4*k+1]=4*k+1;SG[4*k+2]=4*k+2;SG[4*k+3]=4*k+4;SG[4*k+4]=4*k+3;

其实只推导前四项并不能说明什么,或者说哪怕推导出一万项也不能说明什么,因为可能都是数学里面的特例。但是这确实是一个正确的命题,现在我们来证明一下。

对于上面的命题,我们用数学归纳法来证明一下:

首先当k==0的时候上面已经推到过,所以是正确的。也就是归纳法的第一步是正确的。接下来我们假设当k的时候命题成立,下面来证明,当为k+1的时候命题成立。对于一堆石子的n个石子,他可以转化成从0~n-1的任意数量。然后对于4*k+1~4*k+4有他们对应的SG分别是4*k+1~4*k+4所以说明:    对于4*k+5有他能推导成之前0~4*k+4的所有状态,所以SG[4*k+5]=4*k+5;    对于4*k+6有同样的规则,所以SG[4*k+6]=4*k+6;    对于4*k+7有,能推导到0~4*k+6,但是我们注意到他同样可以分解成两堆也即(1,4*k+6),前面我们有SG[4*k+6]=4*k+6,显然4*k+6是一个偶数,所以(4*k+6)^1=4*k+7。所以SG[4*k+7]=4*k+8。    对于4*k+8有,能推导到0~4*k+7,但是我们注意到4*k+7的贡献是4*k+8,而4*k+7轮空了,所以SG[4*k+8]==4*k+7。经过上面的推导我们知道数学归纳成功,说明上面的结论是成立的,然后我们直接用就行了。下面给出ac代码。

【代码】

#include<iostream>#include<cstdio>#include<cstring>#include<string.h>#include<algorithm>#include<vector>#include<cmath>#include<stdlib.h>#include<time.h>#include<stack>#include<set>#include<map>#include<queue>#include<sstream>using namespace std;#define rep0(i,l,r) for(int i = (l);i < (r);i++)#define rep1(i,l,r) for(int i = (l);i <= (r);i++)#define rep_0(i,r,l) for(int i = (r);i > (l);i--)#define rep_1(i,r,l) for(int i = (r);i >= (l);i--)#define MS0(a) memset(a,0,sizeof(a))#define MS1(a) memset(a,-1,sizeof(a))#define MSi(a) memset(a,0x3f,sizeof(a))#define sin1(a) scanf("%d",&(a))#define sin2(a,b) scanf("%d%d",&(a),&(b))#define sll(a) scanf("%lld",&(a))#define sll2(a,b) scanf("%lld%lld",&(a),&(b))#define sdo(a) scanf("%lf",&(a))#define sdo2(a,b) scanf("%lf%lf",&(a),&(b))#define inf 0x3f3f3f3f#define lson i<<1,l,mid#define rson ((i<<1)|1),mid+1,r#define uint unsigned inttypedef pair<int,int> PII;#define A first#define B second#define pb push_back#define MK make_pair#define ll long longtemplate<typename T>void read1(T &m){    T x=0,f=1;    char ch=getchar();    while(ch<'0'||ch>'9')    {        if(ch=='-')f=-1;        ch=getchar();    }    while(ch>='0'&&ch<='9')    {        x=x*10+ch-'0';        ch=getchar();    }    m = x*f;}template<typename T>void read2(T &a,T &b){    read1(a);    read1(b);}template<typename T>void read3(T &a,T &b,T &c){    read1(a);    read1(b);    read1(c);}template<typename T>void out(T a){    if(a>9) out(a/10);    putchar(a%10+'0');}template<typename T>void outn(T a){    if(a>9) out(a/10);    putchar(a%10+'0');    puts("");}using namespace std;///---------------------------------------------------------------------------------int main(){//    freopen("in.txt","r",stdin);    int T;    read1(T);    while(T--)    {        int len,num;        int ans=0;        read1(len);        while(len--)        {            read1(num);            if(num%4==0)                ans^=(num-1);            else if(num%4<=2)                ans^=num;            else                ans^=(num+1);        }        if(ans) puts("Alice");        else puts("Bob");    }    return 0;}