[HDU3032]Nim or not Nim?(博弈Multi-SG游戏)

来源:互联网 发布:php tp框架下载 编辑:程序博客网 时间:2024/05/20 05:25

题目描述

传送门
题意:Alice和Bob轮流取石子,每一次可以从任意一堆中拿走任意个石子,也可以将一堆石子分为两个小堆。先拿完者获胜。

题解

Multi-SG游戏:
这个问题实际上是一个经典的Lasker’s Nim游戏:每一轮允许两会中操作之一:①从一堆石子中取走任意多个②将一堆数量不少于2的石子分成都不为空的两堆。
这个问题可以用SG函数来解决。首先,操作①其实和Nim游戏没什么区别,对于一个石子数为k的点来说,后继可以为0…k-1。而操作②实际上是把一个游戏分成了两个游戏。根据游戏的和的概念,这两个游戏的和应该为两个子游戏的SG函数值的异或。而求某一个点的SG函数要利用它的后继,它的后继就应该为当前局面能产生的所有单一游戏,以及当前局面所有能分成的多个单一游戏的游戏的和。
比如说,状态3的后继有:0、1、2、(1,2),他们的SG值分别为0、1、2、3,所以sg(3) = 4。
那么这样,我们就能用SG函数解决这个问题。
但是,如果数据范围非常大的时候SG函数就不能用了,通过打表可以发现一个更优美的结论:

  • if(x%4==0) sg[x]=x-1;
  • if(x%4==1||x%4==2) sg[x]=x;
  • if(x%4==3) sg[x] = x+1。

那么这就是一道结论题?

代码

#include<iostream>#include<cstring>#include<cstdio>using namespace std;#define N 1000000int T,n,x,ans;int get_sg(int x){    if (x%4==0) return x-1;    else if (x%4==1||x%4==2) return x;    else if (x%4==3) return x+1;}int main(){    scanf("%d",&T);    while (T--)    {        scanf("%d",&n);ans=0;        for (int i=1;i<=n;++i)        {            scanf("%d",&x);            ans^=get_sg(x);        }        if (ans) puts("Alice");        else puts("Bob");    }}
0 0