首页 > 其他分享 >[ABC215D] Coprime 2 题解

[ABC215D] Coprime 2 题解

时间:2023-08-14 19:24:27浏览次数:55  
标签:std ABC215D 题解 valueType father tag Coprime MAX 节点

题意

给定数列 \(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;
}

标签:std,ABC215D,题解,valueType,father,tag,Coprime,MAX,节点
From: https://www.cnblogs.com/User-Unauthorized/p/solution-at-abc215-d.html

相关文章

  • [ARC126D] Pure Straight 题解
    题意给定一个有\(N\)个正整数的序列\(A=(A_1,A_2,\cdots,A_N)\),且\(A_i\in\left[1,K\right]\)。你可以对这个序列做如下操作若干次。交换两个相邻的元素,也就是选出\(i\)和\(j\)满足\(\lverti-j\rvert=1\)并交换\(A_i\)和\(A_j\)。找到最小的操作数使\(......
  • CF793F Julia the snail 题解
    题意有一个长为\(n\)的杆,上面有\(m\)条绳子,每条绳子可以让蜗牛从\(l_i\)爬到\(r_i\)(中途不能离开),保证\(r_i\)各不相同。蜗牛也可以自然下落。现在有\(q\)次询问,询问\(x\)出发,途中高度不能低于\(x\)或高于\(y\),问最高能爬到的位置。\(n,m,q\leq10^5\)。题解......
  • 【免费分享 图书】《阿里云天池大赛赛题解析——机器学习篇》-PDF电子书-百度云
    找这本书的资源简直要把我找吐了,各种网站压缩包一下下来就开始各种套路(比如要你充钱)为了防止还有我这样的受害者,这就把找到的PDF给大家分享一下。链接在文章最后如果这篇文章能够帮到您,麻烦帮我点个赞,并关注一下我,我有更多动力,持续分享更多有用图书给您!非常感谢,不胜感激!(点关......
  • 「题解注释」P3345 [ZJOI2015] 幻想乡战略游戏
    题解P3345【[ZJOI2015]幻想乡战略游戏】-Baka'sBlog-洛谷博客(luogu.org)耗时:半个下午代码注释:#include<bits/stdc++.h>typedeflonglongLL;inlineintrd(){ inta=1,b=0;charc=getchar(); while(!isdigit(c))a=c=='-'?0:1,c=getcha......
  • CF1859C 题解
    思路我们实际上发现它计算的就是\(p_i\cdoti\)的和再减去一个\(p_i\cdoti\)中的最大值。那我们可以枚举这个最大值\(p_x\cdotx\),这个值就是最后和中需要删除的数值。这里我们可以使用贪心。我们可以从\(n\sim1\)枚举除\(p_i\)的每个数字需要配的数字。当然,......
  • CF1859B 题解
    题意给定\(n\)个长度为\(m\)的数组,每个数组可以向别的数组转移最多一个数字,任意一个数组都可以接受无穷多的数字,最大化每个数组的最小值之和。做法考虑贪心。我们记第\(i\)个数组的第\(j\)个数字为\(a_{i,j}\)。我们先对每一个数组按照升序进行排序,那我们最不愿意......
  • 【题解】 Call Me Call Me CCPC Mianyang 2022
    https://codeforces.com/gym/104065/原题做法是类似猫树转成前缀后缀,写起来太麻烦,不如如下做法:如果每个区间所需满足的点不超过\(\sqrt{n}\)个,即可以如下暴力:把每个区间拍到线段树上,每次更新一个点,则在线段树上把所有包含他的区间全部\(-1\)看看是否减到了\(0\),拿个队列一......
  • 问题解答:关于 SAP UI5 控制器(Controller) JavaScript 编码里单引号和双引号的用法澄
    笔者这篇教程文末,有朋友提问:SAPUI5应用开发教程之十-什么是SAPUI5应用的描述符文件manifest.json问题1:在index.html文件中body标签添加了代码:<divdata-sap-ui-componentdata-name="sap.ui5.walkthrough"data-id="container"data-settings='{"id":"wa......
  • ARC129C 题解
    problem&blog。提供一种不一样的做法喵。考虑原问题的逆问题。这个很典,直接前缀和\(sum_i\)表示\([1,i]\)顺次拼接的数\(\bmod7\)的值,那么\([l,r]\)符合条件当且仅当\(sum_r-sum_{l-1}=0\),即\(sum_r=sum_{l-1}\)。设\(c_p=\sum\limits_{i=1}^n[sum_i=p]\),这个逆......
  • 【题解】洛谷 P9532 [YsOI2023] 前缀和
    原题链接【LGR-151-Div.2】洛谷8月月赛II&YsOI2023T1解题思路设有一序列a,其中a1 =a2,第k(≥3) 项为前k-1项的前缀和。可以发现前q项分别为第一项的20 倍,20 倍,21 倍,22 倍,23 倍…2q-3 倍,2q-2 倍。扩展到整个序列中,可得第i( ≥ 3)项一定为2i-2a1。......