题目大意:
Vasya
V
a
s
y
a
和
Petya
P
e
t
y
a
玩一个游戏,每人每次写一个
x∈[1,n]
x
∈
[
1
,
n
]
的正整数。当
x
x
被写后,及
x
x
的任意正整数幂都不能被写,写不了数字的人输。假设为先手,问当两边都是最佳操作时,谁能获胜。
分析:
我们可以把一些能用同样底数的幂次分组,即分成
c,c2,c3....
c
,
c
2
,
c
3
.
.
.
.
,为了让分开的集合相互独立,
c
c
不能表示成其他数(除了)的幂形式。既然已经同底数了,所以可以理解为每次可以取一个数
i∈[1,k]
i
∈
[
1
,
k
]
,当选了
i
i
时,不能选,
k
k
为满足的最大整数,问谁能赢的问题。
因为 n≤1e9 n ≤ 1 e 9 ,所以 230>1e9 2 30 > 1 e 9 ,所以 k<30 k < 30 。对于每一个组,都相当于一个 NIM N I M 游戏,可以用 sg s g 函数求解,再把所有组的 sg s g 异或起来。我们可以打表打出大小为 [1,30] [ 1 , 30 ] 的组的 sg s g 值。
打表代码如下:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <map>
using namespace std;
map <int,int> h;
int a[35],n,g;
int dfs(int x)
{
if ((x==0) || (h[x])) return h[x];
bool b[35];
for (int i=0;i<=n;i++) b[i]=0;
for (int i=1;i<=n;i++)
{
int sub=x;
if ((x&(1<<(i-1)))!=0)
{
for (int j=i;j<=n;j+=i) sub&=g-(1<<(j-1));
b[dfs(sub)]=1;
}
}
int c=0;
for (int i=0;i<=n;i++)
{
if (!b[i])
{
c=i;
break;
}
}
h[x]=c;
return c;
}
int main()
{
for (int i=1;i<=30;i++)
{
n=i;
g=(1<<n)-1;
for (int j=1;j<=i;j++) a[j]=1;
printf("%d\n",dfs(g));
}
}
因为大于 n−−√ n 的数的 k k 值都是,所以我们可以暴力前 n−−√ n 个数的 sg s g 异或和。但是要排除掉那些可以写成其他数幂次(除 c1 c 1 )的数,然后统计一下 [n−−√+1,n] [ n + 1 , n ] 有多少个这样的数,那么这个范围内剩下的数的 sg s g 值都等于 sg[1] s g [ 1 ] 。因为是异或,只要判断剩下的数的奇偶性即可。
代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#define LL long long
const LL sg[31]={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};
const int maxn=1e5+7;
using namespace std;
LL n;
LL ans,v[maxn],t;
int main()
{
scanf("%lld",&n);
LL p=trunc(sqrt(n));
ans=sg[1];
for (LL i=2;i<=p;i++)
{
if (v[i]) continue;
LL c=i,k=1;
while (c*i<=n)
{
c*=i;
if (c<=p) v[c]=1;
else t++;
k++;
}
ans^=sg[k];
}
if ((n-p-t)%2==1) ans^=sg[1];
if (ans==0) printf("Petya");
else printf("Vasya");
}