[luogu p1621] 集合

传送门

集合

题目描述

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

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

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

输入输出格式

输入格式

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

输出格式

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

输入输出样例

输入样例 #1

10 20 3

输出样例 #1

7

说明

样例 1 解释

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

数据规模与约定

  • 对于 \(80\%\) 的数据,\(1 \leq a \leq b \leq 10^3\)
  • 对于 \(100\%\) 的数据,\(1 \leq a \leq b \leq 10^5,2 \leq p \leq b\)

分析

这题的题目都写了大字集合了,疯狂暗示我们用并查集做啊。

那么具体怎么做呢?

首先我们先用欧拉筛筛出一个质数表,然后在质数表中选择 \(\ge p\) 的质数(我们假定其为 \(P\)),用一个循环变量 \(j\) 枚举,向上乘,直到 \(j \times P > b\) 时停止,过程中不断将 \(j \times P\)\(j\) 合并(也就是把 \(P\)\(j\) 倍数字全部合并到一个并查集),最后\(a \sim b\) 中有多少个集合即可,即:满足 \(fa_i = i\)\(a \le i \le b\) 的数有多少个。

上代码啦。

代码

/*
 * @Author: crab-in-the-northeast 
 * @Date: 2020-08-19 00:27:02 
 * @Last Modified by: crab-in-the-northeast
 * @Last Modified time: 2020-08-19 01:13:48
 */
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>

const int maxb = 100005;

bool isprime[maxb];
int prime_num[maxb];
int fa[maxb];

void prime(int n) {//预处理 1 ~ n 的所有质数
    std :: memset(isprime, true, sizeof(isprime));
    isprime[1] = false;
    for (int i = 2; i <= n; ++i) {
        if (isprime[i]) prime_num[++prime_num[0]] = i;
        for (int j = 1; j <= prime_num[0] && i * prime_num[j] <= n; ++j) {
            isprime[i * prime_num[j]] = false;
            if (i % prime_num[j] == 0) break;
        }
    }
}

int find(int x) {
    while (x != fa[x]) x = fa[x] = fa[fa[x]];
    return x;
}

int main() {
    int a, b, p;
    std :: scanf("%d%d%d", &a, &b, &p);
    prime(b);

    for (int i = 1; i <= b; ++i) {
        fa[i] = i;
    }
    
    for (int i = 1; i <= prime_num[0]; ++i) {
        if (prime_num[i] >= p) {
            for (int j = std :: ceil(a * 1.0 / prime_num[i]); j * prime_num[i] <= b; ++j) {
                int fax = find(prime_num[i]);
                int fay = find(j * prime_num[i]);
                fa[fax] = fay;//合并所有prime_num[i] 的 j 倍数字。
            } 
        }
    }

    int ans = 0;
    for (int i = a; i <= b; ++i)
        if (fa[i] == i)
            ++ans;//统计集合数量
    
    std :: printf("%d\n", ans);
    return 0;
}

评测记录

评测记录

posted @ 2020-08-19 14:23  东北小蟹蟹  阅读(70)  评论(0编辑  收藏