「解题报告」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 问题,直接做就行。

此时我们可能得到几种情况:

  1. 有一个人先手必胜;
  2. 两个人都先手必败。

如果是第一种情况,那么肯定不会选等于 \(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;
}
posted @ 2023-01-30 09:32  APJifengc  阅读(142)  评论(1编辑  收藏  举报