SG函数 + 线性筛质数 ACdream 1112

来源:互联网 发布:ubuntu 桥接模式简书 编辑:程序博客网 时间:2024/06/10 22:55

题目来源:

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, 不可以为sum24         vis[Get_SG(i)] = 1 ;25     for(int i = 1 ; i <= n /2 ; i++) // 下一个状态为 分成两堆,不可以为026         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 }

 

0 0
原创粉丝点击