洛谷题单指南-集合-P1621 集合
原题链接:https://www.luogu.com.cn/problem/P1621
题意解读:a~b之间的数,把有大于等于p的公共质因数的数进行合并作为一个集合,求一共有多少个集合。
解题思路:
要进行集合合并、统计集合数,可以使用并查集,有两种做法:
1、暴力法
80%的数据在1000范围内,因此通过双重循环枚举,判断两个数的最大公约数是否大于等于p,是则合并集合,最后统计集合数即可,应该可以得到80分,这里不做详细介绍。
2、素数筛法
进一步分析题意,有大于等于p的公共质因数的即为一个集合,只需要先筛出a~b之间大于等于p的素数,然后对每一个素数,将其倍数进行合并即可,最后统计集合的数量:p[i] == i。
100分代码:
#include <bits/stdc++.h>
using namespace std;
/*
1、筛出a~b之间所有素数
2、从p开始,对于大于等于p的所有素数k,如果2k<=b,将[a,b]范围内k、2k、3k。。。。对应的数标合并到一个集合
3、剩余未标记的元素各为一个集合
*/
const int N = 1e5 + 5;
int primes[N], cnt;
bool flag[N];
int p[N]; //集合
int a, b, t;
//查找x所在集合
int find(int x)
{
if(p[x] == x) return p[x];
return p[x] = find(p[x]);
}
//将x、y合并
void merge(int x, int y)
{
p[find(x)] = find(y);
}
//埃氏筛
void get_primes()
{
for(int i = 2; i <= b; i++)
{
if(!flag[i])
{
if(i >= t) primes[++cnt] = i;
for(int j = i + i; j <= N; j += i)
{
flag[j] = true;
}
}
}
}
int main()
{
cin >> a >> b >> t;
for(int i = a; i <= b; i++) p[i] = i; //初始化集合
get_primes(); //筛出a~b之间大于等于p的素数
for(int i = 1; i <= cnt; i++)
{
int j = primes[i];
while(j < a) j += primes[i]; // 找到第一个大于等于a的以primes[i]为因子的数
int last = j;
while(j + primes[i] <= b)
{
j += primes[i];
merge(last, j); //将以primes[i]为因子的数进行合并
}
}
int ans = 0;
for(int i = a; i <= b; i++)
{
if(p[i] == i) ans++;
}
cout << ans;
return 0;
}