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 John 或 Farmer 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++**实现。