[CodeForces850C]Arpa and a game with Mojtaba
题目大意:
给你一个包含n个数的数列,两个人轮流对数列进行如下操作:
选择一个质数p和一个正整数k,将数列中所有能被p^k整除的数除以p^k。
最后不能操作者负。
问先手是否有必胜策略。
思路:
显然,结果不直接与数列中数的值有关,而与数列中每个数的质因数及其次数有关,因此我们可以将每个质因数分开考虑。
枚举数列中出现的每一个质因数p,对数列中的数除去p^k就相当于将p对应的次数减去k。
如果不同的数对于同一个质因数p,对应的次数相同,那么无论除去p的几次,对于这两个数的影响都是一样的。
那么我们只需要将不同的质数作为我们的子游戏,游戏状态记录p出现次数(即,如果一个数中包含17,一个数中包含17^2,那么就记录1和2)。
极限情况,2^31>1e9,那么对于每一个质数,我们可以用一个int类型状压记录出现次数。
即,若状态s的第i位为1,则p^i在数列中出现。
求SG函数的时候,我们可以发现SG函数的取值仅与出现次数,即状态s有关,而与具体是哪个质数无关。
我们可以从s的最高位枚举,依次考虑把s在i后面的位数减掉的情况,这样,较高的次数在降次以后会加到较低的位数,这一操作可以用位运算(x%si)|(x/si)表示。
对于边界情况,s=1时,表示数列中已经没有这样的质因数,SG值显然是0。
1 #include<cmath> 2 #include<cstdio> 3 #include<cctype> 4 #include<vector> 5 #include<cstring> 6 #include<ext/hash_map> 7 inline int getint() { 8 register char ch; 9 while(!isdigit(ch=getchar())); 10 register int x=ch^'0'; 11 while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0'); 12 return x; 13 } 14 const int N=100; 15 __gnu_cxx::hash_map<int,int> sg; 16 int a[N]; 17 inline int count(int &x,const int &p) { 18 int ret=0; 19 while(!(x%p)) { 20 ret++; 21 x/=p; 22 } 23 return ret; 24 } 25 int getsg(const int x) { 26 if(sg.count(x)) { 27 return sg[x]; 28 } 29 if(x==1) return sg[x]=0; 30 int si=1<<30; 31 while(!(x&si)) si>>=1; 32 int mex[10000]; 33 memset(mex,0,sizeof mex); 34 while(si!=1) { 35 mex[getsg((x%si)|(x/si))]=x; 36 si>>=1; 37 } 38 int tmp=0; 39 while(mex[tmp]==x) tmp++; 40 return sg[x]=tmp; 41 } 42 int main() { 43 int n=getint(); 44 for(int i=0;i<n;i++) { 45 a[i]=getint(); 46 } 47 int ans=0; 48 for(int i=0;i<n;i++) { 49 int tmp=a[i]; 50 for(int j=2;j<=sqrt(tmp);j++) { 51 if(!(tmp%j)) { 52 int s=0; 53 for(int k=0;k<n;k++) { 54 s|=1<<count(a[k],j); 55 } 56 ans^=getsg(s); 57 } 58 } 59 if(a[i]!=1) { 60 int p=a[i]; 61 int s=0; 62 for(int k=0;k<n;k++) { 63 s|=1<<count(a[k],p); 64 } 65 ans^=getsg(s); 66 } 67 } 68 puts(ans?"Mojtaba":"Arpa"); 69 return 0; 70 }