「解题报告」ARC155D Avoid Coprime Game
还不错的题,但是这场的 A, B, C 出的是真的恶心。分类讨论你马呢。
可以改写一下过程,改成选择一个数后将所有 \(a_j \gets \gcd(a_i, a_j)\),不难发现结果不变,而且这样所有 \(a_i \le G\)。
首先有一个朴素的想法:假如当前 \(G\) 为某数,那么所有小于 \(G\) 的数都没被碰过。
但是有一个问题:我们不知道之前选了多少等于 \(G\) 的数,而下一步可能能够选择等于 \(G\) 的没选的数。
我们先不考虑大于等于 \(G\) 的数,只考虑小于它的数。那这就是一个经典博弈 DP 问题,直接做就行。
此时我们可能得到几种情况:
- 有一个人先手必胜;
- 两个人都先手必败。
如果是第一种情况,那么肯定不会选等于 \(G\) 的数,因为选了就把回合给了对方,结果没啥区别。
而如果是第二种情况,两个人可以一直选等于 \(G\) 的数,最后谁先不能选 \(G\) 了谁就 G 了。
于是我们发现,我们只关心之前选的 \(G\) 的个数的奇偶性,所以只记录这个奇偶性即可。
当前有多少个等于 \(G\) 的数很好处理,看有多少数是 \(G\) 的倍数即可,主要来考虑当前 \(G\) 能转移到哪里,也就是求所有的 \(\gcd(G, a_i)\)。
考虑设 \(f_{G, b}\) 表示能够使 \(\gcd(G, a_i) = b\) 的数的个数,我们可以容斥求这个东西,\(f_{G, b} = \sum [b | a_i] - \sum_{b | c, b \ne c} f_{G, c}\)。
枚举一步倍数是 \(O(n \log n)\) 的,枚举两步就是 \(O(n \log^2 n)\),所以总复杂度 \(O(n \log^2 n)\)。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 200005;
int n, m, a[MAXN];
vector<int> fac[MAXN], g[MAXN];
int cnt[MAXN];
bool f[MAXN][2];
int h[MAXN];
int tmp[MAXN];
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
cnt[a[i]]++;
}
m = *max_element(a + 1, a + 1 + n);
for (int i = m; i > 1; i--) {
for (int j = i; j <= m; j += i) {
fac[j].push_back(i);
tmp[i] += cnt[j] > 0;
}
}
for (int i = 2; i <= m; i++) {
for (int a : fac[i]) {
h[a] += tmp[a];
for (int b : fac[a]) if (a != b) {
h[b] -= h[a];
}
}
for (int a : fac[i]) {
if (h[a]) {
if (i != a) {
g[i].push_back(a);
}
h[a] = 0;
}
}
}
for (int i = 2; i <= m; i++) {
for (int j = i + i; j <= m; j += i) {
cnt[i] += cnt[j];
}
}
for (int i = 2; i <= m; i++) {
for (int j : g[i]) {
f[i][0] |= !f[j][1];
f[i][1] |= !f[j][0];
}
if (!f[i][0] && !f[i][1]) {
f[i][cnt[i] % 2] = 1;
}
}
for (int i = 1; i <= n; i++) {
printf(f[a[i]][0] ? "Aoki\n" : "Takahashi\n");
}
return 0;
}