[ABC215D] Coprime 2 题解

发布时间 2023-08-14 19:19:26作者: User-Unauthorized

题意

给定数列 \(A_N\) 和一个正整数 \(M\),求出所有的 \(1 \le k \le M\) 满足 \(\forall i \in \left[1,N\right],\gcd(k, A_i) = 1\)

题解

本题存在线性复杂度算法。

\(\operatorname{lpf}(n) = [1 < n] \min\{p : p \mid n\} + [1 = n]\),即 \(n\) 的最小质因数。特别地,\(n=1\) 时,其值为 \(1\)

考虑线性筛筛出合数的过程,合数 \(n\) 会被 \(\dfrac{n}{\operatorname{lpf}(n)}\) 筛掉,从 \(n\) 向其连边,可以发现最终会形成一棵树。建树过程的复杂度为 \(\mathcal{O}(N)\),接下来我们在这棵树上进行操作。

首先对数列 \(A_n\) 的每个元素 \(A_i\),在这棵树上的对应节点打上一个 \(tag\),然后我们暴力向上跳,给路过的每个节点和边打上 \(tag\),其中给边 \(\left(u, pa_u\right)\)\(tag\) 直接给 \(\operatorname{lpf}(u)\) 对应的节点打上 \(tag\) 即可,如果我们跳到已经打过 \(tag\) 的点那么就停止向上跳。其中我们是通过给边打 \(tag\) 来处理出在这个数列中所有作为质因子出现的质数,给节点打 \(tag\) 是为了避免多余的处理,可以看出每条边最多被处理一次,所以这部分复杂度也是线性。

现在我们已经处理出了所有在这个数列中出现的质因子,易证符合题目条件的 \(k\) 就是不含有这其中任意一个质因子的数,所以我们下面考虑将 \(tag\) 下方,只要一条边对应的质数被打上了 \(tag\),那么所有到根节点路径上有这条边的数都含有该质因子,所以我们考虑从上而下的下方 \(tag\),同时可以断定 \(pa_u < u\),即 \(u\) 节点的父节点值一定比 \(u\) 小,所以从小到大遍历节点,考虑父亲节点是否打上 \(tag\) 和 当前节点与父亲节点相连的边是否打上 \(tag\) 即可,同上面一样,给节点打 \(tag\) 一是为了记录答案,二是可以减少不必要的重复遍历,这部分中每条边最多被处理一次,所以这部分复杂度也是线性。总复杂度也为线性。

考虑到本题数据过水,不能体现出线性算法的效率,所以造了一个加强版:T358758

Code

//A
#include <bits/stdc++.h>

typedef int valueType;
typedef std::vector<valueType> ValueVector;
typedef std::vector<bool> bitset;

constexpr valueType MAX = 1e5 + 5;

int main() {
    std::ios_base::sync_with_stdio(false);

    valueType N, M;

    std::cin >> N >> M;

    ValueVector source(N);

    for (auto &iter: source)
        std::cin >> iter;

    ValueVector father(MAX + 1, 1);

    bitset tag(MAX + 1, true);

    ValueVector primeList;

    primeList.reserve(std::ceil((long double) MAX / std::log((long double) MAX)));

    for (valueType i = 2; i <= MAX; ++i) {
        if (__builtin_expect(tag[i], false)) {
            father[i] = 1;
            primeList.emplace_back(i);
        }

        for (auto const &iter: primeList) {
            valueType const t = iter * i;

            if (t > MAX)
                break;

            tag[t] = false;
            father[t] = i;

            if (__builtin_expect(i % iter == 0, false))
                break;
        }
    }

    std::fill(tag.begin(), tag.end(), false);

    typedef std::function<void(valueType)> TagFunction;

    TagFunction solve = [&father, &tag](valueType n) {
        while (n > 1 && !tag[n]) {
            tag[n] = true;
            tag[n / father[n]] = true;
            n = father[n];
        }
    };

    for (auto const &iter: source)
        solve(iter);

    tag[1] = false;
    for (valueType i = 2; i <= MAX; ++i)
        tag[i] = tag[i] || (tag[father[i]] || tag[i / father[i]]);


    ValueVector result;

    result.reserve(M);

    result.push_back(1);

    for (valueType i = 2; i <= M; ++i)
        if (!tag[i])
            result.push_back(i);

    std::cout << result.size() << '\n';

    for (auto const &iter: result)
        std::cout << iter << '\n';

    std::cout << std::flush;

    return 0;
}