题目大意:
解题思路:给定n个数字。两个人轮流玩游戏。把这n个数字变成n的因子(不包括本身),最后全变成1就赢了,问你谁会赢,假设Alice赢了,把第一步选择的那一个数字的序号输出,假设有多种方案,输出序号小的。
解题代码:sg[1]=0;
sg[2]=mex{sg[1]}=1;
sg[3]=mex{sg[1]}=1;
sg[4]=mex{sg[2],sg[1]}=2;
sg[5]=mex{sg[1]}=1;
sg[6]=mex{sg[2],sg[3]}=2;
sg[7]=mex{sg[1],sg[7]}=1;
sg[8]=mex{sg[1],sg[2],sg[4]}=3;
..........................
发现 sg[x]为 x因子的个数。
证明:由于 假设一个为a, 则:sg[x]=sg[(x/a)*a]=mex{sg[1]....,sg[(x/a)]}=sg[(x/a)]+1;
求出每一个数字的sg后,仅仅须要看sg的异或和,假设==0 和明显,输出Bob
否则。输出Alice,可是要输出Alice先走了哪一步,这里实用到了一个性质:仅仅须要用合并后的SG值与每一堆SG值分别异或,看得到的结果是否小于原来该堆的SG值。假设小则能够取该堆。
这个性质临时不是非常懂,先用上,以后了解了会给出证明和过程。
#include <iostream> #include <cstdio> #include <vector> #include <cstring> using namespace std; const int maxn=5000010; int n,sg[maxn]; int d[110000]; vector <int> prime; bool isprime[maxn]; void ini(){ isprime[2]=true; for(int i=3;i<maxn;i+=2) isprime[i]=true; for(int i=3;i<maxn;i+=2){ for(int j=i;j<maxn/i;j+=2){ isprime[i*j]=false; } } for(int i=2;i<=maxn;i++){ if(isprime[i]) prime.push_back(i); } } int SG(int x){ if(sg[x]!=-1) return sg[x]; int ret=0,size=prime.size(),tmp=x; for(int i=0;i<size && prime[i]*prime[i]<=x;i++){ while(x%prime[i]==0){ x/=prime[i]; ret++; } } if(x>1) ret++; return sg[tmp]=ret; } int main(){ ini(); memset(sg,-1,sizeof(sg)); int casen=0; while(scanf("%d",&n)!=EOF){ int ans=0; for(int i=0;i<n;i++){ scanf("%d",&d[i]); ans^=SG(d[i]); } if(ans==0) printf("Test #%d: Bob ",++casen); else{ for(int i=0;i<n;i++){ if( ( ans^SG(d[i]) ) < SG(d[i]) ){ printf("Test #%d: Alice %d ",++casen,i+1); break; } } } } return 0; }