集合
题目描述
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 imes P > b) 时停止,过程中不断将 (j imes 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
", ans);
return 0;
}