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[iprime[j]]

然后考虑步数
胜方尽可能拖延,即看那些 S G [ i − p r i m e [ j ] ] = = 0 SG[i-prime[j]]==0 SG[iprime[j]]==0的,找一个最小的 f [ i − p r i m e [ j ] ] f[i-prime[j]] f[iprime[j]]来转移即可

必败方则找一个最大的 f [ i − p r i m e [ j ] ] f[i-prime[j]] f[iprime[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;
}
posted @ 2021-11-10 07:48  lahlah  阅读(51)  评论(0编辑  收藏  举报