Circular Barn (USACO Silver 2022 December)
这是个有一定思考难度的博弈论题目,需要用到素数筛。
- 如果是质数,立即获胜
- 所有能被4整除的情况是必败态,否则是必胜态
- 如果处于必败态,最优选择是每次取2,对手也会取2,因此经过/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;
}