洛谷题解——P1621 集合

题目相关

题目链接

洛谷,https://www.luogu.com.cn/problem/P1621

MYOJ,http://47.110.135.197/problem.php?id=5342

题目描述

Caima 给你了所有 [a,b] 范围内的整数。一开始每个整数都属于各自的集合。每次你需要选择两个属于不同集合的整数,如果这两个整数拥有大于等于 p 的公共质因数,那么把它们所在的集合合并。

重复如上操作,直到没有可以合并的集合为止。

现在 Caima 想知道,最后有多少个集合。

输入格式

一行,共三个整数 a, b, p 用空格隔开。

输出格式

一个数,表示最终集合的个数。

输入样例

10 20 3

输出样例

7

样例解释

对于样例给定的数据,最后有 {10,20,12,15,18},{13},{14},{16},{17},{19},{11} 共 7 个集合,所以输出应该为 7。

数据范围

1 ≤ a ≤ b ≤ 10^5, 2 ≤ p ≤ b。

题解报告

题意分析

题意还是比较清晰的,不需要分析了。

看完后,就知道考察并查集的知识点。

解题思路

写出 p 到 b 的所有素数。可以使用欧拉筛,或者最基本的素数判定方法。

逐一遍历这些素数,找出从 a 到 b 之间的倍数,将这些数放在同一个集合中。

从 a 到 b 遍历,查找集合的个数。

样例数据分析

根据输入样例,我们知道 a=10,b=20,p=3。

初始化并查集

我们使用 STL 的 map 来保存数据,这样并查集的初始值为:

map<int, int> ds;
ds[10] = 10;
ds[11] = 11;
ds[12] = 12;
ds[13] = 13;
ds[14] = 14;
ds[15] = 15;
ds[16] = 16;
ds[17] = 17;
ds[18] = 18;
ds[19] = 19;
ds[20] = 20;

p 到 b 的素数

这样,我们可以写出 p 到 b 的素数为:3, 5, 7, 11, 13, 17, 19。下面我们开始遍历素数,合并集合。

3 的倍数

在 [10, 20] 区间中,3 的倍数有:12, 15, 18。将这三个数变成一个集合 {12, 15, 18}。这样并查查集 ds 变成:

ds[10] = 10;
ds[11] = 11;
ds[12] = 12;
ds[13] = 13;
ds[14] = 14;
ds[15] = 12;
ds[16] = 16;
ds[17] = 17;
ds[18] = 12;
ds[19] = 19;
ds[20] = 20;

5 的倍数

在 [10, 20] 区间中,5 的倍数有:10, 15, 20。由于 15 既是 3 的倍数也是 5 的倍数,因此这个集合要上上面的集合合并,成为 {12, 15, 18, 10, 20}。这样并查查集 ds 变成:

ds[10] = 12;
ds[11] = 11;
ds[12] = 12;
ds[13] = 13;
ds[14] = 14;
ds[15] = 12;
ds[16] = 16;
ds[17] = 17;
ds[18] = 12;
ds[19] = 19;
ds[20] = 12;

7 的倍数

在 [10, 20] 区间中,7 的倍数有:14。因此这个集合要上上面的集合合并,成为 {14}。这样并查查集 ds 变成:

ds[10] = 12;
ds[11] = 11;
ds[12] = 12;
ds[13] = 13;
ds[14] = 14;
ds[15] = 12;
ds[16] = 16;
ds[17] = 17;
ds[18] = 12;
ds[19] = 19;
ds[20] = 12;

11 的倍数

在 [10, 20] 区间中,11 的倍数有:11。因此这个集合要上上面的集合合并,成为 {11}。这样并查查集 ds 变成:

ds[10] = 12;
ds[11] = 11;
ds[12] = 12;
ds[13] = 13;
ds[14] = 14;
ds[15] = 12;
ds[16] = 16;
ds[17] = 17;
ds[18] = 12;
ds[19] = 19;
ds[20] = 12;

13 的倍数

在 [10, 20] 区间中,13 的倍数有:13。因此这个集合要上上面的集合合并,成为 {13}。这样并查查集 ds 变成:

ds[10] = 12;
ds[11] = 11;
ds[12] = 12;
ds[13] = 13;
ds[14] = 14;
ds[15] = 12;
ds[16] = 16;
ds[17] = 17;
ds[18] = 12;
ds[19] = 19;
ds[20] = 12;

17 的倍数

在 [10, 20] 区间中,17 的倍数有:17。因此这个集合要上上面的集合合并,成为 {17}。这样并查查集 ds 变成:

ds[10] = 12;
ds[11] = 11;
ds[12] = 12;
ds[13] = 13;
ds[14] = 14;
ds[15] = 12;
ds[16] = 16;
ds[17] = 17;
ds[18] = 12;
ds[19] = 19;
ds[20] = 12;

19 的倍数

在 [10, 20] 区间中,19 的倍数有:19。因此这个集合要上上面的集合合并,成为 {19}。这样并查查集 ds 变成:

ds[10] = 12;
ds[11] = 11;
ds[12] = 12;
ds[13] = 13;
ds[14] = 14;
ds[15] = 12;
ds[16] = 16;
ds[17] = 17;
ds[18] = 12;
ds[19] = 19;
ds[20] = 12;

到这里位置,我们已经完成并集。下面我们开始遍历 [a, b] 查询其中有几个集合。

查询集合数

根据上面的数据,我们知道 ds[i]=i 表示这是一个集合,因此我们可以统计出集合数为:7。

技术细节

可以使用欧拉筛来加速素数判定。

AC 参考代码

//http://47.110.135.197/problem.php?id=5342
//https://www.luogu.com.cn/problem/P1621
//P1621 集合
#include <bits/stdc++.h>

using namespace std;

map<int, int> ds;
map<int, int> ranks;
vector<int> primes;

bool isPrime(int x) {
    for (int i=2; i*i<=x; i++) {
        if (0==x%i) {
            return false;
        }
    }
    return true;
}

int find_root(int x) {
    return x==ds[x]?x:ds[x]=find_root(ds[x]);
}

int union_set(int x, int y) {
    int x_root=find_root(x);
    int y_root=find_root(y);
    if (x_root==y_root) {
        return 0;
    } else {
        if (ranks[x_root]>ranks[y_root]) {
            ds[y_root]=x_root;
        } else if (ranks[x_root]<ranks[y_root]) {
            ds[x_root]=y_root;
        } else {
            ds[x_root]=y_root;
            ranks[y_root]++;
        }
        return 1;
    }
}

int main() {
    int a,b,p;
    cin>>a>>b>>p;

    //初始化并查集
    for (int i=a; i<=b; i++) {
        ds[i]=i;
        ranks[i]=0;
    }

    //列出p到b之间的所有素数
    for (int i=p; i<=b; i++) {
        if (true==isPrime(i)) {
            primes.push_back(i);
        }
    }

    //并集
    for (int i=0; i<primes.size(); i++) {
        int st=ceil(1.0*a/primes[i])*primes[i];
        for (int j=st+primes[i]; j<=b; j+=primes[i]) {
            union_set(st, j);
        }
    }

    //查询有几个集合
    int ans=0;
    for (int i=a; i<=b; i++) {
        if (ds[i]==i) {
            ans++;
        }
    }
    cout<<ans<<"\n";

    return 0;
}

猜你喜欢

转载自blog.csdn.net/justidle/article/details/108866511