本文已参与「新人创作礼」活动,一起开启掘金创作之路。
题目链接:Nim or not Nim? - HDU 3032 - Virtual Judge (ppsucxtt.cn)
题目大意:给你n堆石子,两个人轮流进行操作,每次可以从任意一堆中取走任意数量的石子,也可以将一堆石子分成两堆,最后无法操作者输。
这一看就是一个尼姆博弈的变形题目,尝试过后发现这道题目无法利用尼姆博弈本身的一些结论,我们只能利用sg函数进行求解,在求解sg函数时与尼姆博弈中求解sg函数过程差不多,就是多了一个分解操作,对于有n个石子的一堆石子,如果是一般的尼姆博弈的话,他的后继状态可能有1~n-1个石子的一堆,而对于本题而言,他的后继状态多了一些,对于任意的x+y=n,(x!=0,y!=0),都有可能成为他的后继,所以我们要考虑上这些情况的sg值,考虑上这些情况再求sg就可以了,但一看数据范围是1e6,所以显然是有具体的结论的,我们先将n小于等于100的情况打印出来,发现具有如下规律:
因此我们可以直接利用此规律求出每堆石子的sg值并进行异或即可,下面是代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
using namespace std;
//const int N=1e6+10;
//bool vis[N+10];
//int sg[N],f[N+10];
//void init()
//{
// memset(sg,0,sizeof sg);
// for(int i=1;i<=N;i++)
// {
// memset(vis,false,sizeof vis);
// for(int j=1;f[j]<=i;j++)
// vis[sg[i-f[j]]]=1;
// for(int j=1;j<i;j++)
// vis[sg[j]^sg[i-j]]=1;
// for(int j=0;j<=N;j++)
// {
// if(!vis[j])
// {
// sg[i]=j;
// break;
// }
// }
// }
//}
int sg(int n)
{
if(n%4==3) return n+1;
else if(n%4==0) return n-1;
else return n;
}
int main()
{
// for(int i=1;i<=N;i++) f[i]=i;
// init();
// for(int i=0;i<=N;i++)
// printf("%d\n",sg[i]);
int T,n,t;
cin>>T;
for(int i=1;i<=T;i++)
{
scanf("%d",&n);
int ans=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&t);
ans^=sg(t);
}
if(ans) puts("Alice");
else puts("Bob");
}
return 0;
}