题意:
有1~n,n个数字,两个人轮流操作,每一次一个人可以拿一个数字$x$,之后$x, x^2, x^3....x^t$全都被删掉。
给定n,问最优策略下谁赢。
解法:
考虑SG函数,可以注意到题目中取走$x$后,$x^2,x^3...$不可以取,类似石子合并问题。
对于1~n的数字可以分为两类:
1.不存在$x^t, t>1$,可以视为一堆只有一块石头的石子堆,$SG(1) = 0$。
2.存在$x^t, t>1$。
对于第一种情况,直接记录有多少个x满足条件即可,分奇偶讨论。
对于第二种情况,显然有$t<=30$,可以注意到最终$SG$值和$x$无关,这样打表预处理$sg(t)$表示$x^1,x^2...x^t$
对应的$SG$值,打表直接用$O(2^30)$状压即可(注意到$sg(t)<=30$,所以用char类型的sg数组即可节省空间)。
实际上有效的状态并不多,所以只要几秒钟(意外的快)。
最后求NIM和即可。
总效率$O(sqrt{n}*logn)$
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 5 #define N 1000010 6 #define LL long long 7 8 using namespace std; 9 10 bool flag[N]; 11 int n; 12 int SG[]={0,1,2,1,4,3,2,1,5,6,2,1,8,7,5,9,8,7,3,4,7,4,2,1,10,9,3,6,11,12,14}; 13 14 int main() 15 { 16 scanf("%d",&n); 17 int sum=n; 18 int ans=0; 19 for(LL i=2;i*i<=n;i++) 20 { 21 if(flag[i]) continue; 22 int tmp=0; 23 for(LL x=i;x<=n;x*=i) 24 { 25 if(x*x<=n) flag[x]=1; 26 tmp++; 27 } 28 ans^=SG[tmp]; 29 sum-=tmp; 30 } 31 ans ^= sum&1; 32 if(ans==0) puts("Petya"); 33 else puts("Vasya"); 34 return 0; 35 }