Circular Barn (USACO Silver 2022 December)

Circular Barn (USACO Silver 2022 December)

这是个有一定思考难度的博弈论题目,需要用到素数筛。

  1. 如果是质数,立即获胜
  2. 所有能被4整除的情况是必败态,否则是必胜态
  3. 如果处于必败态,最优选择是每次取2,对手也会取2,因此经过/4轮后会输
  4. 如果处于必胜态,取最大的质数使得剩下的数能被4整除 --> 这可以通过对所有小于v且模4为1或3的质数进行二分查找来实现
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
 
int T, n;
int a[200005];  // <= 5e6
 
bool p[5000005];
vector<int> primes;  // all primes < 10000
vector<int> primes_mod_1;
vector<int> primes_mod_3;
 
void prepare() {
    // prime sieve
    memset(p, true, sizeof(p));
    for (int i = 2; i*i <= 5000000; i++) {
        if (p[i]) {
            for (int j = i*i; j <= 5000000; j += i)
                p[j] = false;
        }
    }
    primes.push_back(1);
    for (int i = 2; i <= 5000000; i++) {
        if (p[i]) {
            primes.push_back(i);
            if (i % 4 == 1)
                primes_mod_1.push_back(i);
            else if (i % 4 == 3)
                primes_mod_3.push_back(i);
        }
    }
}
 
int main() {
    scanf("%d", &T);
 
    prepare();
 
    int m = 0;
    for (int t = 0; t < T; t++) {
        scanf("%d", &n);
        for (int i = 0; i < n; i++) {
            scanf("%d", &a[i]);
            m = max(m, a[i]);
        }
 
        if (n == 2) {
            printf("Farmer John\n"); continue;
        } else if (n == 3) {
            printf("Farmer Nhoj\n"); continue;
        }
 
        bool john = false;
        int lose_rounds = INT32_MAX;    // john loses after this many rounds
        int win_rounds = INT32_MAX;     // john wins after this many rounds
        int win_pos, lose_pos;
        for (int i = 0; i < n; i++) {
            int v = a[i];
            if (p[v]) {
                win_rounds = 0;
                win_pos = i;
                break;
            }
            if (v % 4 == 0) {
                if (v / 4 < lose_rounds) {
                    lose_rounds = v / 4;
                    lose_pos = i;
                }
            } else if (v % 4 == 2) {    // john takes 2, then johns wins after /4 rounds
                if (v / 4 < win_rounds) {
                    win_rounds = v / 4;
                    win_pos = i;
                }
            } else {                    // non-prime odd: find largest prime p s.t. (v-p)%4==0
                int p = 0;
                if (v % 4 == 1) {
                    p = *prev(lower_bound(primes_mod_1.begin(), primes_mod_1.end(), v));
                } else {
                    p = *prev(lower_bound(primes_mod_3.begin(), primes_mod_3.end(), v));
                }
                if ((v-p) / 4 < win_rounds) {
                    win_rounds = (v-p)/4;
                    win_pos = i;
                }
            }
        }
        if (win_rounds < lose_rounds || win_rounds == lose_rounds && win_pos < lose_pos)
            printf("Farmer John\n");
        else
            printf("Farmer Nhoj\n");
    }
 
    return 0;
}