E. Not a Nim Problem
Two players, Alice and Bob, are playing a game. They have $n$ piles of stones, with the $i$-th pile initially containing $a_i$ stones.
On their turn, a player can choose any pile of stones and take any positive number of stones from it, with one condition:
- let the current number of stones in the pile be $x$. It is not allowed to take from the pile a number of stones $y$ such that the greatest common divisor of $x$ and $y$ is not equal to $1$.
The player who cannot make a move loses. Both players play optimally (that is, if a player has a strategy that allows them to win, no matter how the opponent responds, they will win). Alice goes first.
Determine who will win.
Input
The first line contains a single integer $t$ ($1 \le t \le 10^4$) — the number of test cases.
Each test case consists of two lines:
- the first line contains a single integer $n$ ($1 \le n \le 3 \cdot 10^5$);
- the second line contains $n$ integers $a_1, a_2, \dots, a_n$ ($1 \le a_i \le 10^7$).
Additional constraint on the input: the sum of $n$ across all test cases does not exceed $3 \cdot 10^5$.
Output
For each test case, output Alice if Alice wins, or Bob if Bob wins.
Example
Input
3
3
3 2 9
4
3 3 6 1
5
1 2 3 4 5
Output
Bob
Alice
Bob
解題思路
借這題學一下 SG 函式。SG 函式是解決像取石子這類博弈論問題的一大利器。
以取石子游戲為例,我們把每一堆石子可能發生的變化用 DAG 來表示,其中節點代表一個局面,邊代表可以從一個局面轉移到另一個局面。比如以本題的規則對 $3$ 堆石子 $[1,3,4]$ 進行遊戲得到的 DAG 如下:
每個節點(局面)$u$ 的 SG 值就是其後繼節點 $v_1, \ldots v_m$ 的 SG 值的 $\mathrm{mex}$ 值,即 $\mathrm{SG}(u) = \mathrm{mex}\{ \mathrm{SG}(v_1), \ldots, \mathrm{SG}(v_m) \}$,其中定義集合 $S$ 的 $\mathrm{mex}$ 值為不屬於 $S$ 中的最小非負整數。上圖中每個節點的 SG 值如下所示:
可以發現每一個 DAG 都只有一個起點 $S$,因此如果 $\mathrm{SG}(S) \ne 0$ 則先手必勝,否則 $\mathrm{SG}(S) = 0$ 則先手必敗。這是因為任意 SG 不為 $0$ 的節點都可以走到 SG 為 $0$ 的節點,而任意 SG 為 $0$ 的節點都走不到 SG 不為 $0$ 的節點(否則當前節點的 SG 值就大於 $0$ 了),又因為所有的終點(即出度為 $0$ 的點)的 SG 值為 $0$,對應著先手必敗,因此結論得證。
而如果有 $n$ 個 DAG,每個 DAG 的起點分別為 $S_1, \ldots, S_n$,那麼整個遊戲先手必勝的條件是 $\mathrm{SG}(S_1) \oplus \cdots \oplus \mathrm{SG}(S_n) \ne 0$,否則 $\mathrm{SG}(S_1) \oplus \cdots \oplus \mathrm{SG}(S_n) = 0$ 則先手必敗。證明的方法與 Nim 遊戲十分相似。
回到本題,令 $A = \max\{ a_i \}$,顯然我們需要求出 $1 \sim A$ 的 SG 值,然後根據 $\mathrm{SG}(a_1) \oplus \cdots \oplus \mathrm{SG}(a_n)$ 判斷先手必勝還是必敗,時間複雜度為 $O(A^2 \log{A})$。由於 $A$ 最大取 $10^7$,因此這種做法顯會然超時。
我們先打表看看 SG 值是否滿足某些規律,以下是求出 $1 \sim 100$ 的 SG 值的程式和結果:
打表程式碼
#include <bits/stdc++.h>
using namespace std;
const int N = 105;
int f[N];
int dfs(int u) {
if (f[u] != -1) return f[u];
set<int> st;
for (int i = 1; i <= u; i++) {
if (__gcd(i, u) == 1) st.insert(dfs(u - i));
}
for (int i = 0; true; i++) {
if (!st.count(i)) return f[u] = i;
}
}
int main() {
memset(f, -1, sizeof(f));
for (int i = 1; i <= 100; i++) {
cout << "SG(" << i << ") = " << dfs(i) << '\n';
}
return 0;
}
打表結果
SG(1) = 1
SG(2) = 0
SG(3) = 2
SG(4) = 0
SG(5) = 3
SG(6) = 0
SG(7) = 4
SG(8) = 0
SG(9) = 2
SG(10) = 0
SG(11) = 5
SG(12) = 0
SG(13) = 6
SG(14) = 0
SG(15) = 2
SG(16) = 0
SG(17) = 7
SG(18) = 0
SG(19) = 8
SG(20) = 0
SG(21) = 2
SG(22) = 0
SG(23) = 9
SG(24) = 0
SG(25) = 3
SG(26) = 0
SG(27) = 2
SG(28) = 0
SG(29) = 10
SG(30) = 0
SG(31) = 11
SG(32) = 0
SG(33) = 2
SG(34) = 0
SG(35) = 3
SG(36) = 0
SG(37) = 12
SG(38) = 0
SG(39) = 2
SG(40) = 0
SG(41) = 13
SG(42) = 0
SG(43) = 14
SG(44) = 0
SG(45) = 2
SG(46) = 0
SG(47) = 15
SG(48) = 0
SG(49) = 4
SG(50) = 0
SG(51) = 2
SG(52) = 0
SG(53) = 16
SG(54) = 0
SG(55) = 3
SG(56) = 0
SG(57) = 2
SG(58) = 0
SG(59) = 17
SG(60) = 0
SG(61) = 18
SG(62) = 0
SG(63) = 2
SG(64) = 0
SG(65) = 3
SG(66) = 0
SG(67) = 19
SG(68) = 0
SG(69) = 2
SG(70) = 0
SG(71) = 20
SG(72) = 0
SG(73) = 21
SG(74) = 0
SG(75) = 2
SG(76) = 0
SG(77) = 4
SG(78) = 0
SG(79) = 22
SG(80) = 0
SG(81) = 2
SG(82) = 0
SG(83) = 23
SG(84) = 0
SG(85) = 3
SG(86) = 0
SG(87) = 2
SG(88) = 0
SG(89) = 24
SG(90) = 0
SG(91) = 4
SG(92) = 0
SG(93) = 2
SG(94) = 0
SG(95) = 3
SG(96) = 0
SG(97) = 25
SG(98) = 0
SG(99) = 2
SG(100) = 0
可以發現以下規律:
- 如果 $x$ 是偶數,那麼 $\mathrm{SG}(x) = 0$。
- 如果 $x$ 是從 $3$ 開始的質數,那麼 $\mathrm{SG}(x) = p(x)$,其中 $p(x)$ 指 $x$ 在所有質數中的第幾個。
- 如果 $x$ 是奇合數,那麼 $\mathrm{SG}(x) = \mathrm{SG}(d(x))$,其中 $d(x)$ 指 $x$ 的最小質因子。
- 特別的,$\mathrm{SG}(1) = 1$,$\mathrm{SG}(2) = 0$。
因此我們就可以用尤拉篩以 $O(A)$ 的複雜度求出 $1 \sim A$ 的 SG 值。
AC 程式碼如下,時間複雜度為 $O(A)$:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e7 + 5;
int primes[N], cnt;
bool vis[N];
int f[N];
void get_prime(int n) {
for (int i = 2; i <= n; i++) {
if (!vis[i]) {
primes[cnt++] = i;
f[i] = cnt;
}
for (int j = 0; primes[j] * i <= n; j++) {
int t = primes[j] * i;
vis[t] = true;
if (t & 1) f[t] = f[primes[j]];
if (i % primes[j] == 0) break;
}
}
f[1] = 1, f[2] = 0;
}
void solve() {
int n;
cin >> n;
int ret = 0;
while (n--) {
int x;
cin >> x;
ret ^= f[x];
}
cout << (ret ? "Alice" : "Bob") << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
get_prime(N - 1);
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
參考資料
第四章 數學知識(四):https://www.acwing.com/file_system/file/content/whole/index/content/4810/
公平組合遊戲 - OI Wiki:https://oi-wiki.org/math/game-theory/impartial-game/
Educational Codeforces Round 169 Editorial:https://codeforces.com/blog/entry/132790