题目来源:
http://acdream.info/problem?pid=1112
分析:
对于 n 个数, 相当于n 个局面, 只要将 n 个局面 异或 就好, 所以我们只考虑 一个数的时候。
一个数 x = p1^r1 * p2^r2 * ... pm ^rm
则 X 的质因子个数 sum = r1 + r2 +... rm
SG函数 转移时 , 一种是 sum - > 1...sum -1 , 此时不可以为 sum(因为题意 不准转移为其本身) , 另一种 是 将 sum 分成 两子堆, 此时堆不为空。
求sum时, 线性筛素数时候顺便将每个数的最小的质数因子筛出来,从小到大先预处理,然后求sum(n)就等于sum(n/least[n])+1。
代码如下:
1 const int N = 5000010 ; 2 int pri[N] , sum[N] , sg[55] ; 3 bool vis[N] ; 4 // sum[i] 表示 i 所有质因子的个数 5 6 void get_prime(int n){ 7 int num = 0 , i , j ; 8 sum[1] = 0 ; 9 memset(vis, 0 , sizeof(vis)) , vis[1] = 0 ; 10 for(i = 2 ; i <= n ; i++){ 11 if(!vis[i]) pri[num ++] = i , sum[i] = 1 ; 12 for(j = 0 ; j < num && i * pri[j] <= n ; j++){ 13 vis[i *pri[j]] = 1 ; 14 sum[i * pri[j]] = sum[i] + 1 ; 15 if( i % pri[j] == 0) break ; 16 } 17 } 18 } 19 20 int Get_SG(int n){ 21 bool vis[55] = {0} ; 22 if(sg[n] != -1) return sg[n] ; 23 for(int i = 0 ; i < n ; i++) // 下一个状态 sum -> 0 ...sum-1, 不可以为sum 24 vis[Get_SG(i)] = 1 ; 25 for(int i = 1 ; i <= n /2 ; i++) // 下一个状态为 分成两堆,不可以为0 26 vis[Get_SG(i) ^ Get_SG(n - i)] = 1 ; 27 for(int i = 0 ; ; i++) 28 if(!vis[i]) return sg[n] = i ; 29 } 30 31 int main(){ 32 int n , x ; 33 get_prime(5000000) ; 34 memset(sg, -1 , sizeof(sg)) ; sg[0] = 0 ; 35 for(int i = 1 ; i<= 50 ; i++) 36 sg[i] = Get_SG(i) ; 37 while(scanf("%d" , &n) != EOF){ 38 int ans = 0 ; 39 for(int i = 1 ; i<= n ; i++){ 40 scanf("%d" , &x) ; 41 ans ^= sg[sum[x]] ; 42 } 43 if(ans) puts("Alice") ; 44 else puts("Bob") ; 45 } 46 return 0 ; 47 }