本题的关键是要发现可以把数字分成很多组,每组为{x^1,x^2,...,x^n},那么问题就是求出每组的sg函数,然后异或得解。又可以发现每组的sg值只和n有关,而且n的值小于30。那么我们就可以暴力求出1到30对应的sg函数。暴力求解时枚举去掉那个数字,求出所有子状态sg的mex值即可。我用一个数去状压有哪些数,再加个map去记忆化搜索。具体见代码:
#include <cstdio>
#include <algorithm>
#include <map>
#include <set>
#include <cstring>
#include <vector>
#define MOD 1000000007
using namespace std;
bool vis[40000];
map<int,int>mp;
__int64 POW(__int64 a,int b)
{
__int64 res=1;
while(b)
{
if(b&1)
res=res*a;
a=a*a;
b>>=1;
}
return res;
}
int dfs(int x)
{
if(mp.find(x)!=mp.end())
return mp[x];
if(x==0)
return mp[x]=0;
bool vis[40]={0};
int tmp=0,mx=29;
for(;mx>=0;mx--)
if(x&(1<<mx))
break;
for(int i=1;i<=mx;i++)
{
if((x&(1<<i))==0)
continue;
tmp=0;
for(int j=1;j<=mx;j++)
{
if((x&(1<<j))==0)
continue;
if(j%i==0)
continue;
tmp|=(1<<j);
}
vis[dfs(tmp)]=1;
}
for(int i=0;;i++)
{
if(vis[i]==0)
{
mp[x]=i;
return i;
}
}
}
int SG[30]={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};
int main()
{
/*
find SG:
SG[0]=0;
for(int i=1;i<30;i++)
{
int tmp=0;
for(int j=1;j<=i;j++)
tmp|=(1<<j);
SG[i]=dfs(tmp);
printf("%d ",SG[i]);
}
*/
int n;
scanf("%d",&n);
int sg=0,rest=n;
for(int i=2;i*i<=n;i++)
{
if(vis[i])
continue;
int cnt=0;
for(int j=1;POW(i,j)<=n;j++)
{
int k=POW(i,j);
cnt++;
if((__int64)k*k<=n)
vis[k]=1;
}
sg=sg^SG[cnt];
rest-=cnt;
}
rest%=2;
sg=sg^rest;
if(sg)
puts("Vasya");
else
puts("Petya");
//system("pause");
return 0;
}