【巴什博弈 线性筛】P8901 [USACO22DEC] Circular Barn S|普及+

P8901 [USACO22DEC] Circular Barn S

题目描述

Farmer John 和他的死对头 Farmer Nhoj 在一个环形牛棚内玩游戏。牛棚内有 N ( 1 ≤ N ≤ 10 5 ) N(1 \le N \le 10^5) N(1≤N≤105) 个房间,第 i i i 个房间初始时内有 a i a_i ai 头奶牛 ( 1 ≤ a i ≤ 5 × 10 6 ) (1 \le a_i \le 5 \times 10^6) (1≤ai≤5×106)。游戏玩法如下:

  • 两位农夫将总是在同一个房间里。进入一个房间后,每位农夫各执行一次行动,Farmer John 在先。两位农夫在游戏开始时进入房间 1 1 1。
  • 如果当前房间中的奶牛数量为零,则此时执行行动的农夫即失败。否则,执行行动的农夫选择一个整数 P P P,其中 P P P 为 1 1 1 或一个不超过当前房间中奶牛的数量的质数,并从当前房间中移除 P P P 头奶牛。
  • 两位农夫均完成行动之后,两位农夫移动到环形牛棚的下一间房间。也就是说,如果农夫们在房间 i i i,那么他们会移动到房间 i + 1 i+1 i+1,除非他们在房间 N N N,在这种情况下他们会移动到房间 1 1 1。

当两位农夫均采用最优策略时,求获胜的农夫。

输入格式

输入包含 T T T 个子测试用例。输入的第一行包含 T ( 1 ≤ T ≤ 1000 ) T(1 \le T \le 1000) T(1≤T≤1000)。下面是 T T T 个子测试用例。

每个子测试用例的第一行包含 N N N,第二行包含 a 1 , ⋯   , a N a_1, \cdots ,a_N a1,⋯,aN。

输入保证所有 N N N 之和不超过 2 × 10 5 2 \times 10^5 2×105。

输出格式

对于每一个子测试用例,输出获胜的农夫,为 Farmer JohnFarmer Nhoj 之一。

输入输出样例 #1

输入 #1

复制代码
5
1
4
1
9
2
2 3
2
7 10
3
4 9 4

输出 #1

复制代码
Farmer Nhoj
Farmer John
Farmer John
Farmer John
Farmer Nhoj

说明/提示

样例 1 解释

对于第一个子测试用例,Farmer John 可以从第一个房间中移除 1 1 1、 2 2 2 或 3 3 3 头奶牛。无论他移除多少头奶牛,Nhoj 都可以移除剩余的奶牛,迫使 FJ 在他们绕回第一个房间时失败。

对于第二个子测试用例,FJ 可以移除 5 5 5 头奶牛,迫使 Nhoj 面对剩下的 4 4 4 头奶牛。现在,Nhoj 可以移除 1 1 1、 2 2 2 或 3 3 3 奶牛。现在的情况类似于第一个子测试用例。

对于第三个和第四个子测试用例,FJ 可以立即移除第一个房间的所有奶牛,使 Nhoj 失败。

对于第五个子测试用例,FJ 可以从第一个房间中移除 1 1 1

、 2 2 2 或 3 3 3 奶牛,然后 Nhoj 可以随后移除余下的奶牛。当他们绕回第一个房间时,FJ 将会失败。

测试点性质

  • 测试点 2 − 4 2-4 2−4 满足 N = 1 N=1 N=1。
  • 测试点 1 , 2 , 5 − 7 1,2,5-7 1,2,5−7 满足 a i ≤ 1000 a_i \le 1000 ai≤1000。
  • 测试点 8 − 20 8-20 8−20 没有额外限制。

[USACO22DEC] Circular Barn S 巴什博弈 线性筛

考虑只有一个房间。
性质一 :如果牛的数量是4的倍数,则先手方必败。其它情况,先手方必胜。如果是4的倍数,先手方选取x,后手方选取4-(x%4)。这样先手方的奶牛数量永远是4的倍数,直到0。如果先手方的奶牛a[i]不是4的倍数,则选择a[i]%4后,后手方必败。

性质二:如果先手方必败,能支持多少回合。4i不是质数,无法选择。选择4i+1,对方选择7。选择2,对方只能选择2,其它都不是质数。选择4i+3,对方选择5。故寻找2回合数最长。即:a[i]第a[i]/2次行动时失败。a[0]为0和4,分别是0,2。
性质三:如果先手方必胜,则只能选择4
i+(a[i]%4),否则对方会反败为胜。第(1 + 余下的数量/2)次行动时结束。要想早结束,余下的数要尽快的小,选择取的质数

要尽可能得大。用线性筛记录所有4i+1,4i+2,4i+3的质数,求小于等于a[i]的最大质数

b[i]记录各房间失败时的行动次数。bm=(b[i])/2最小的b[i],bm是奇数先手方胜利,否则先手方失败!
时间复杂度:O(nlogn)

代码

核心代码

cpp 复制代码
#include <iostream>
#include <sstream>
#include <vector>
#include<map>
#include<unordered_map>
#include<set>
#include<unordered_set>
#include<string>
#include<algorithm>
#include<functional>
#include<queue>
#include <stack>
#include<iomanip>
#include<numeric>
#include <math.h>
#include <climits>
#include<assert.h>
#include<cstring>
#include<list>
#include<array>

#include <bitset>
using namespace std;

template<class T1, class T2>
std::istream& operator >> (std::istream& in, pair<T1, T2>& pr) {
	in >> pr.first >> pr.second;
	return in;
}

template<class T1, class T2, class T3 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3>& t) {
	in >> get<0>(t) >> get<1>(t) >> get<2>(t);
	return in;
}

template<class T1, class T2, class T3, class T4 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3, T4>& t) {
	in >> get<0>(t) >> get<1>(t) >> get<2>(t) >> get<3>(t);
	return in;
}

template<class T = int>
vector<T> Read() {
	int n;
	cin >> n;
	vector<T> ret(n);
	for (int i = 0; i < n; i++) {
		cin >> ret[i];
	}
	return ret;
}
template<class T = int>
vector<T> ReadNotNum() {
	vector<T> ret;
	T tmp;
	while (cin >> tmp) {
		ret.emplace_back(tmp);
		if ('\n' == cin.get()) { break; }
	}
	return ret;
}

template<class T = int>
vector<T> Read(int n) {
	vector<T> ret(n);
	for (int i = 0; i < n; i++) {
		cin >> ret[i];
	}
	return ret;
}

template<int N = 1'000'000>
class COutBuff
{
public:
	COutBuff() {
		m_p = puffer;
	}
	template<class T>
	void write(T x) {
		int num[28], sp = 0;
		if (x < 0)
			*m_p++ = '-', x = -x;

		if (!x)
			*m_p++ = 48;

		while (x)
			num[++sp] = x % 10, x /= 10;

		while (sp)
			*m_p++ = num[sp--] + 48;
		AuotToFile();
	}
	void writestr(const char* sz) {
		strcpy(m_p, sz);
		m_p += strlen(sz);
		AuotToFile();
	}
	inline void write(char ch)
	{
		*m_p++ = ch;
		AuotToFile();
	}
	inline void ToFile() {
		fwrite(puffer, 1, m_p - puffer, stdout);
		m_p = puffer;
	}
	~COutBuff() {
		ToFile();
	}
private:
	inline void AuotToFile() {
		if (m_p - puffer > N - 100) {
			ToFile();
		}
	}
	char  puffer[N], * m_p;
};

template<int N = 1'000'000>
class CInBuff
{
public:
	inline CInBuff() {}
	inline CInBuff<N>& operator>>(char& ch) {
		FileToBuf();
		while (('\r' == *S) || ('\n' == *S) || (' ' == *S)) { S++; }//忽略空格和回车
		ch = *S++;
		return *this;
	}
	inline CInBuff<N>& operator>>(int& val) {
		FileToBuf();
		int x(0), f(0);
		while (!isdigit(*S))
			f |= (*S++ == '-');
		while (isdigit(*S))
			x = (x << 1) + (x << 3) + (*S++ ^ 48);
		val = f ? -x : x; S++;//忽略空格换行		
		return *this;
	}
	inline CInBuff& operator>>(long long& val) {
		FileToBuf();
		long long x(0); int f(0);
		while (!isdigit(*S))
			f |= (*S++ == '-');
		while (isdigit(*S))
			x = (x << 1) + (x << 3) + (*S++ ^ 48);
		val = f ? -x : x; S++;//忽略空格换行
		return *this;
	}
	template<class T1, class T2>
	inline CInBuff& operator>>(pair<T1, T2>& val) {
		*this >> val.first >> val.second;
		return *this;
	}
	template<class T1, class T2, class T3>
	inline CInBuff& operator>>(tuple<T1, T2, T3>& val) {
		*this >> get<0>(val) >> get<1>(val) >> get<2>(val);
		return *this;
	}
	template<class T1, class T2, class T3, class T4>
	inline CInBuff& operator>>(tuple<T1, T2, T3, T4>& val) {
		*this >> get<0>(val) >> get<1>(val) >> get<2>(val) >> get<3>(val);
		return *this;
	}
	template<class T = int>
	inline CInBuff& operator>>(vector<T>& val) {
		int n;
		*this >> n;
		val.resize(n);
		for (int i = 0; i < n; i++) {
			*this >> val[i];
		}
		return *this;
	}
	template<class T = int>
	vector<T> Read(int n) {
		vector<T> ret(n);
		for (int i = 0; i < n; i++) {
			*this >> ret[i];
		}
		return ret;
	}
	template<class T = int>
	vector<T> Read() {
		vector<T> ret;
		*this >> ret;
		return ret;
	}
private:
	inline void FileToBuf() {
		const int canRead = m_iWritePos - (S - buffer);
		if (canRead >= 100) { return; }
		if (m_bFinish) { return; }
		for (int i = 0; i < canRead; i++)
		{
			buffer[i] = S[i];//memcpy出错			
		}
		m_iWritePos = canRead;
		buffer[m_iWritePos] = 0;
		S = buffer;
		int readCnt = fread(buffer + m_iWritePos, 1, N - m_iWritePos, stdin);
		if (readCnt <= 0) { m_bFinish = true; return; }
		m_iWritePos += readCnt;
		buffer[m_iWritePos] = 0;
		S = buffer;
	}
	int m_iWritePos = 0; bool m_bFinish = false;
	char buffer[N + 10], * S = buffer;
};

class CCreatePrime {
public:
	CCreatePrime(int iMax) :m_isPrime(iMax + 1, true)
	{
		m_isPrime[0] = m_isPrime[1] = false;
		for (int i = 2; i <= iMax; i++)
		{
			if (m_isPrime[i])
			{
				m_vPrime.emplace_back(i);
			}
			for (const auto& n : m_vPrime)
			{
				if ((long long)n * i > iMax) { break; }
				m_isPrime[n * i] = false;
				if (0 == i % n) { break; }
			}
		}
	}
	vector<int> m_vPrime;
	vector<bool> m_isPrime;
};
class Solution {
public:
	bool Ans(const vector<int>& a) {
		static auto pri = Prime();
		vector<int> b;
		for (const auto& i : a) {
			if (0 == i % 4) {
				b.emplace_back(i / 2);
			}
			else {
				const auto& v = pri[i % 4];
				auto it = upper_bound(v.begin(), v.end(), i);
				--it;
				b.emplace_back(1 + (i - *it) / 2);
			}
		}
		int iMin = 0;
		for (int i = 1; i < b.size(); i++) {
			if (b[i] / 2 < b[iMin] / 2) {
				iMin = i;
			}
		}
		return b[iMin] & 1;
	}
	static vector<vector<int>> Prime() {
		CCreatePrime cp(5'000'000);
		vector<vector<int>> pri(4);
		pri[1].emplace_back(1);
		for (auto& i : cp.m_vPrime) {
			pri[i % 4].emplace_back(i);
		}
		return pri;
	}
};

int main() {
#ifdef _DEBUG
	freopen("a.in", "r", stdin);
#endif // DEBUG	
	//ios::sync_with_stdio(0); cin.tie(nullptr);
	CInBuff<> in; COutBuff<10'000'000> ob;
	int T;
	in >> T;
	for (int i = 0; i < T; i++)
	{
		auto b = in.Read<int>();
		auto res = Solution().Ans(b);
		cout << (res? "Farmer John":"Farmer Nhoj") << "\n";
	}
#ifdef _DEBUG		
	//printf("D=%d", D);
	//Out(b, ",b=");
	//Out(que, "que=");
	//Out(B, "B=");
	//Out(que, "que=");
	//Out(B, "B=");
#endif // DEBUG	
	
	return 0;
}

单元测试

cpp 复制代码
TEST_METHOD(TestMethod11)
		{
		
			auto res = Solution().Ans({ 4 });
			AssertEx(false, res);
		}
		TEST_METHOD(TestMethod12)
		{
			auto res = Solution().Ans({ 9 });
			AssertEx(true, res);
		}
		TEST_METHOD(TestMethod13)
		{
			auto res = Solution().Ans({ 2,3 });
			AssertEx(true, res);
		}
		TEST_METHOD(TestMethod14)
		{
			auto res = Solution().Ans({ 7,10 });
			AssertEx(true, res);
		}
		TEST_METHOD(TestMethod15)
		{
			auto res = Solution().Ans({ 4 ,9 ,4 });
			AssertEx(false, res);
		}

扩展阅读

我想对大家说的话
亲士工具箱:支持AutoCad2013及以上
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作
活到老,学到老。明朝中后期,大约50%的进士能当上堂官(副部及更高);能当上堂官的举人只有十余人。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。

视频课程

先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771

如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

测试环境

操作系统:win7 开发环境: VS2019 C++17

或者 操作系统:win10 开发环境: VS2022 C++17

如无特殊说明,本算法用**C++**实现。

相关推荐
样例过了就是过了1 小时前
LeetCode热题100 电话号码的字母组合
数据结构·c++·算法·leetcode·dfs
Yusei_05232 小时前
C++17入门
c++
biubiuibiu2 小时前
选择适合的硬盘:固态与机械硬盘的对比与推荐
c++·算法
LXS_3572 小时前
案例 —— 机房预约系统
开发语言·c++·学习方法
yeflx2 小时前
C++纯虚接口
开发语言·c++
Tisfy2 小时前
LeetCode 1415.长度为 n 的开心字符串中字典序第 k 小的字符串:DFS构造 / 数学O(n)
数学·算法·leetcode·深度优先·字符串·dfs·模拟
永远睡不够的入2 小时前
C++list详解
c++·windows·list
仰泳的熊猫2 小时前
题目2268:蓝桥杯2016年第七届真题-密码脱落
数据结构·c++·算法·蓝桥杯
山栀shanzhi3 小时前
C++ 核心机制解析:#pragma once 与 extern 的具体职责与区别
开发语言·c++·面试