石子游戏 (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<=100N<=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");
	}
}
posted @ 2017-04-09 18:09  Jacky#50  阅读(257)  评论(0编辑  收藏  举报