题目来源:

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 }