P1621 集合题解

时间:2021-08-07
本文章向大家介绍P1621 集合题解,主要包括P1621 集合题解使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

题目传送门

一、思路与感悟

1、查看一下A、B集合的数据范围,发现是\(1 <= a < b <= 10^5\),双重循环遍历所有组合,就是\(10^{10}\)次运算,肯定会\(TLE\),所以暴力是不行的。
2、那只能是通过某些条件干掉一些数字,减小范围。题意可知,两个数都需要有一个大于等于\(p\)的公共质数因子,需要从这里入手。
3、利用线筛(欧拉筛)获取到\(b\)范围内的所有质数。然后再通过循环,可以找到\(a \sim b\) 之间的所有质数。
4、利用埃筛的思想,把每一个找出来的质数,成倍的增加,直到大于\(b\)终止,这样,就可以找出所以符合条件的数字。
(1)在\(a \sim b\)之间。
(2)有大于等于\(p\)的公共质数因子。

5、把这些数字存储到一个变长数组中。
6、将这个变长数组中的每一个符合条件的数字合并并查集。
7、找出最后并查集的个数,输出。

二、C++代码

#include <bits/stdc++.h>

using namespace std;
const int N = 1e5 + 10;
/**
 测试用例:
 10 20 3
 答案:
 7
 */

//欧拉筛
int primes[N], cnt;     // primes[]存储所有素数
bool st[N];             // st[x]存储x是否被筛掉
void get_primes(int n) {
    for (int i = 2; i <= n; i++) {
        if (!st[i]) primes[cnt++] = i;
        for (int j = 0; primes[j]*i <= n; j++) {
            st[primes[j] * i] = true;
            if (i % primes[j] == 0) break;
        }
    }
}

//并查集数组
int fa[N];

//要深入理解这个递归并压缩的过程
int find(int x) {
    if (fa[x] != x) fa[x] = find(fa[x]);
    return fa[x];
}

//加入家族集合中
void join(int c1, int c2) {
    int f1 = find(c1), f2 = find(c2);
    if (f1 != f2)fa[f1] = f2;
}


//读入一个开始和结束,a:开始,b:结束
int a, b, p, ans;

int main() {
    //读入
    cin >> a >> b >> p;

    //并查集初始化
    for (int i = a; i <= b; i++) fa[i] = i;

    //筛出b以内的所有素数
    get_primes(b);

    //遍历筛出的质数表,找到每一个大于等于p的质数,然后把这个质数在y范围内的倍数找出来,并加入到动态数组v中
    for (int i = 0; i < cnt; i++) {
        if (primes[i] >= p) {
            //将具有公共质数因子的数,放到一个数组v中
            vector<int> v;
            for (int j = 1; j * primes[i] <= b; j++)//找出质数倍数
                if (j * primes[i] >= a && j * primes[i] <= b)
                    v.push_back(j * primes[i]);
            //把数组v中的元素进行并查集关联
            for (int j = 1; j < v.size(); j++)
                join(v[j - 1], v[j]);
        }
    }
    //并查集个数
    for (int i = a; i <= b; i++) if (fa[i] == i) ans++;
    //输出
    cout << ans << endl;
    return 0;
}

原文地址:https://www.cnblogs.com/littlehb/p/15111819.html