璟雯院

珺璟如晔,雯华若锦

用户工具

站点工具


icpc:problems:usaco22dec_circular_barn_s
problems
名称Circular Barn S
题目编号USACO22DEC_S2
题目链接luogu.com.cn/…
来源USACO
算法分类博弈论, 数学
难易程度看懂题解

Circular Barn S

想法

最小的合数为4,分类讨论$a \le 4$的情况:

  1. 当a为4时,先手必输;
  2. 当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,取完的轮次不同。现在讨论轮次:

  1. 当$ a \% 4 == 0$的情况,先手希望能拖延轮次,故会取2,后手会为了保持模4余数为0的情况,也会取2。该情况的轮次为 a / 2;
  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 由 温婕莺