石子游戏 (SG函数)
题目
Problem Description
Alice 和 Bob 总喜欢聚在一起玩游戏(T_T),今天他(她)们玩的是一款新型的取石子游戏。游戏一开始有N堆石子,Alice 和 Bob 轮流取出石子。在每次操作中,游戏者必须选择其中的一堆石子,并作出下列的其中一种操作:
(1)移去整堆石子
(2)假设石子堆中有X颗石子,取出Y颗石子,其中1<=Y<X
,并且X和Y的最大公约数是1。
游戏结束的条件是:取出最后一颗石子的人胜出。众所周知,Alice和Bob都是绝顶聪明的,假设他们在游戏中都采取最优的策略,问最后谁会胜出游戏呢?
Input
第一行包含一个整数T,表示测试数据的组数。
接下来T组测试数据,在每组数据中,第一行包含一个整数N,表示有多少堆石子。第二行N个正整数,分别表示每堆有多少颗石子。
100%的数据,T<=100
,N<=100
,每堆石子数量不大于1000000
Output
每组测试数据输出一行,表示获胜者的名字(Alice 或者 Bob)。
Sample Input
3
3
3 5 6
4
2 3 6 9
5
3 2 1 1000000 999999
Sample Output
Alice
Bob
Alice
分析
题目意思相当于是有 n 堆石子,每次取与某一堆当前石子数互质的石子个数或全取完。取掉最后一颗石子的人获胜。
这种博弈题一般用SG函数做,写写画画发现,SG(x)=rank(d),其中d为x的最小质因子,rank(d)表示d在质数表中的排名+1,之后把每一堆初始石子个数的SG值异或一下,答案判断是否为零就行了。
用了一下线性筛来筛质数,这样顺便就求出了每个SG函数值。(每个数只会被筛到一次)
程序
#include <cstdio>
int p[1000010],f[1000010],SG[1000010],num,n,ans,k,T;
void get_p(){
for (int i=2; i<1000010; i++){
if(!f[i]) p[++num]=i,SG[i]=num+1;
for (int j=1; j<=num && (j==1 || i%(p[j-1])) && p[j]*i<1000010; j++)
f[p[j]*i]=-1,SG[p[j]*i]=j+1;
}
}
int main(){
SG[1]=1; get_p();
scanf("%d",&T);
for (; T; T--,ans=0){
for (scanf("%d",&n); n--;) ans^=SG[(scanf("%d",&k),k)];
printf(ans ? "Alice\n":"Bob\n");
}
}