[AtCoderContest010D]Decrementing
[AtCoderContest010D]Decrementing
试题描述
There are \(N\) integers written on a blackboard. The \(i\)-th integer is \(A_i\), and the greatest common divisor of these integers is \(1\).
Takahashi and Aoki will play a game using these integers. In this game, starting from Takahashi the two player alternately perform the following operation:
-
Select one integer on the blackboard that is not less than \(2\), and subtract \(1\) from the integer.
-
Then, divide all the integers on the black board by \(g\), where \(g\) is the greatest common divisor of the integers written on the blackboard.
-
The player who is left with only \(1\)s on the blackboard and thus cannot perform the operation, loses the game. Assuming that both players play optimally, determine the winner of the game.
两个人玩游戏,他们轮流操作一个初始时最大公约数为 \(1\) 的序列,一次操作是将一个数减 \(1\),然后所有数除以它们的最大公约数,最终无法操作者输,问是否先手必胜。
输入
The input is given from Standard Input in the following format:
N
A_1 A_2 … A_N
输出
If Takahashi will win, print First
. If Aoki will win, print Second
.
输入示例1
3
3 6 7
输出示例1
First
输入示例2
4
1 2 4 8
输出示例2
First
输入示例3
5
7 8 8 8 8
输出示例3
Second
数据规模及约定
\(1 \le N \le 10^5\)
\(1 \le Ai \le 10^9\)
The greatest common divisor of the integers from \(A_1\) through \(A_N\) is \(1\).
题解
可能考虑奇偶性是一类博弈问题的思路吧。
首先,如果开始时全是奇数,注意到每次操作不会改变数的奇偶性,所以先手一定会将一个奇数变成偶数,那么后手就可以将这个偶数变回奇数,直到最终都变成了 \(1\)(全是奇数),所以后手必胜。
类似地,可以推广出:奇数个偶数,先手必胜;偶数个偶数,后手必胜。
但是有一个小 bug,如果开始时只有一个奇数,那么就不一定了,因为先手可能将那个奇数变成偶数,然后除以一下公约数,变成一个新局面。
当然这种情况很好处理,直接暴力模拟,直到“只有一个奇数”的局面消失,或者变成了全 \(1\) 序列,模拟次数不会超过 \(log_2max\{A_i\}\)。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
int read() {
int x = 0, f = 1; char c = getchar();
while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
return x * f;
}
#define maxn 100010
int n, ceven, A[maxn];
int gcd(int a, int b) { return b ? gcd(b, a % b) : a; }
int main() {
int sum = 0; bool has1 = 0, cur = 0;
n = read();
for(int i = 1; i <= n; i++) A[i] = read(), ceven += !(A[i] & 1), has1 |= (A[i] == 1), (sum += A[i] - 1) &= 1;
if(has1) return puts(sum ? "First" : "Second"), 0;
if(ceven & 1) return puts("First"), 0;
if(n - ceven > 1) return puts("Second"), 0;
for(; ;) {
cur ^= 1;
for(int i = 1; i <= n; i++) if(A[i] & 1) A[i]--;
int g = A[1];
for(int i = 2; i <= n; i++) g = gcd(g, A[i]);
ceven = sum = has1 = 0;
for(int i = 1; i <= n; i++) A[i] /= g, ceven += !(A[i] & 1), has1 |= (A[i] == 1), (sum += A[i] - 1) &= 1;
if(has1) return puts((sum ^ cur) ? "First" : "Second"), 0;
if(n - ceven > 1) return puts(((ceven & 1) ^ cur) ? "First" : "Second"), 0;
}
return 0;
}