icpc:problems:usaco22dec_circular_barn_s
problems | |
---|---|
名称 | Circular Barn S |
题目编号 | USACO22DEC_S2 |
题目链接 | luogu.com.cn/… |
来源 | USACO |
算法分类 | 博弈论, 数学 |
难易程度 | 看懂题解 |
Circular Barn S
想法
最小的合数为4,分类讨论$a \le 4$的情况:
- 当a为4时,先手必输;
- 当a为1, 2, 3是,后手必输。
对于$a \% 4 == 0$,先手也必输,因为后手可以保证每次取完后依旧$a%4==0$,最后a为4时同上述讨论一致。对于$a \% 4 != 0$,先手必赢,因为先手可以将a取至$ a \% 4 == 0 $ 的情况,后手取完后依旧保持$ a \%4 != 0 $的情况,最后a为1,2,3中的一种,同上述讨论一致。
题中提供n个a,对于每个a,取完的轮次不同。现在讨论轮次:
- 当$ a \% 4 == 0$的情况,先手希望能拖延轮次,故会取2,后手会为了保持模4余数为0的情况,也会取2。该情况的轮次为 a / 2;
- 当$ a \% 4 != 0$的情况,先手希望能最快的取完最好,故存在一个最大的质数p,其中p与a同余。这样先手可以保证取完p后a模4余数为0,此情况后手必输。先手会先取p,对于后手来说希望拖延轮次,故接下来两人均会取2。该情况的轮次为 $\frac{a - p}{2} + 1$,其中+1为最开始取p的先手轮。
由于题目的特殊要求,每次取完后会进入下一房间,故需要找到轮次中/2的最小值,且最小值靠前。这样在轮回时,能保证该最小值为最后一个轮到的房间。
代码实现
#include<cstdio> #include<vector> using namespace std; const int N = 5 * 1e6 + 10; vector<int>P[4]; bool f[N]; void init() { vector<int>pline; for (int i = 2; i < N; ++i) { if(!f[i]){ pline.push_back(i); } for (auto p : pline) { if(i * p >= N) break; f[i * p] = true; if(i % p == 0) break; } } for (auto p : pline) { P[p % 4].push_back(p); } return ; } int main() { int T, n; scanf("%d", &T); init(); while(T--) { scanf("%d", &n); vector<int>line(n); for (int i = 0; i < n; ++i) { scanf("%d", &line[i]); } int ans = N, relans; for (auto i : line) { int ans_temp; if(i % 4 == 0) { ans_temp = i / 2; } else { int l = 0, r = P[i % 4].size() - 1, mid; while(l < r) { mid = (l + r + 1) / 2; if(P[i % 4][mid] <= i) l = mid; else r = mid - 1; } ans_temp = (i - P[i % 4][l]) / 2 + 1; } if(ans_temp/2 < ans/2) { ans = ans_temp; relans = ans_temp; } } if(relans % 2) printf("Farmer John\n"); else printf("Farmer Nhoj\n"); } return 0; }
/app/www/public/data/pages/icpc/problems/usaco22dec_circular_barn_s.txt · 最后更改: 2023/06/13 11:54 由 温婕莺