luogu P1857 质数取石子
https://www.luogu.com.cn/problem/P1857
SG函数经典题
首先每个人都必胜必败态是可以DP出来的
S G [ i ] ∣ = ! S G [ i − p r i m e [ j ] ] SG[i]|=!SG[i-prime[j]] SG[i]∣=!SG[i−prime[j]]
然后考虑步数
胜方尽可能拖延,即看那些
S
G
[
i
−
p
r
i
m
e
[
j
]
]
=
=
0
SG[i-prime[j]]==0
SG[i−prime[j]]==0的,找一个最小的
f
[
i
−
p
r
i
m
e
[
j
]
]
f[i-prime[j]]
f[i−prime[j]]来转移即可
必败方则找一个最大的 f [ i − p r i m e [ j ] ] f[i-prime[j]] f[i−prime[j]]来转移
code:
#include<bits/stdc++.h>
#define N 200050
using namespace std;
int prime[N], sz, vis[N], sg[N], f[N];
void init(int n) {
for(int i = 2; i <= n; i ++) {
if(!vis[i]) prime[++ sz] = i;
for(int j = 1; prime[j] * i <= n && j <= sz; j ++) {
vis[prime[j] * i] = 1;
if(i % prime[j] == 0) break;
}
}
prime[++ sz] = n + 1;
sg[0] = sg[1] = 0;
for(int i = 2; i <= n; i ++) {
for(int j = 1; prime[j] <= i; j ++) if(!sg[i - prime[j]]) {
sg[i] = 1;
break;
}
}
for(int i = 2; i <= n; i ++) {
if(sg[i]) f[i] = n + 1;
else f[i] = 0;
if(sg[i]) {
for(int j = 1; prime[j] <= i; j ++)
if(!sg[i - prime[j]]) f[i] = min(f[i], f[i - prime[j]] + 1);
} else {
for(int j = 1; prime[j] <= i; j ++)
f[i] = max(f[i], f[i - prime[j]] + 1);
}
}
}
int n, t;
int main() {
init(20000);
scanf("%d", &t);
while(t --) {
scanf("%d", &n);
if(!sg[n]) printf("-1\n");
else printf("%d\n", f[n]);
}
return 0;
}