原題連結: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;
}