洛谷 P1621:集合 ← 埃氏筛或欧拉筛 + 并查集

【题目来源】
https://www.luogu.com.cn/problem/P1621

【题目描述】
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。

【数据范围】
对于80%的数据,1≤a≤b≤10^3。
对于100%的数据,1≤a≤b≤10^5,2≤p≤b。

【算法分析】
两个数 x、y ∈ [a,b],若存在素数 q ≥ p,且 q 能同时整除 x 和 y,则 x 和 y 属于同一个集合
步骤 1:筛出 [p, b] 范围内的所有素数
步骤 2:初始化并查集
步骤 3:对每个 ≥p 的素数 q,合并其在 [a,b] 内的所有倍数

/*Iterate through all numbers greater than or equal to base,
filter out prime numbers, and combine their multiples.*/
for(int i=base; i<=b; i++) {
    if(!st[i]) continue; //For i is not a prime number, so skip it
    //Iterate over all multiples of i (j=i,2i,3i,...,<=b)
    for(int j=i; j<=b; j+=i) {
        /*j>=a ensures that it is within the range.
        j+i<=b ensures that the next multiple of j is also within the range.*/
        if(j>=a && j+i<=b) merge(j,j+i);
    }
}

步骤 4:统计集合数量

● 并查集:https://blog.csdn.net/hnjzsyjyj/article/details/120147618

int find(int x) {
    if(x!=pre[x]) pre[x]=find(pre[x]);
    return pre[x];
}
 
void merge(int x,int y) {
    int a=find(x);
    int b=find(y);
    if(a!=b) pre[a]=b;
}

●​​​​​​​ 埃氏筛:https://blog.csdn.net/hnjzsyjyj/article/details/157992542

jii

vector<bool> st; //isPrime
vector<int> p; //prime
 
void e_sieve(int n) { //eratosthenes_sieve
    st.assign(n+1,true); //0~n
    st[0]=st[1]=false;
    for(int i=2; i*i<=n; i++) {
        if(!st[i]) continue;
        for(int j=i*i; j<=n; j+=i) st[j]=false;
    }
 
    for(int i=2; i<=n; i++) {
        if(st[i]) p.push_back(i);
    }
}

● 欧拉筛:https://blog.csdn.net/hnjzsyjyj/article/details/158039681

欧拉筛

typedef long long LL;
vector<bool> st; //isPrime
vector<int> p; //prime
 
void euler_sieve(int n) {
    st.assign(n+1,true); //0~n
    st[0]=st[1]=false;
    for(int i=2; i<=n; i++) {
        if(st[i]) p.push_back(i);
        for(int j=0; (LL)p[j]*i<=n; j++) {
            st[p[j]*i]=false;
            if(i%p[j]==0) break;
        }
    }
}

【算法代码】

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
const int maxn=1e5+5;
int pre[maxn];
int a,b,base;
vector<bool> st; //isPrime
vector<int> p; //prime

void euler_sieve(int n) {
    st.assign(n+1,true); //0~n
    st[0]=st[1]=false;
    for(int i=2; i<=n; i++) {
        if(st[i]) p.push_back(i);
        for(int j=0; (LL)p[j]*i<=n; j++) {
            st[p[j]*i]=false;
            if(i%p[j]==0) break;
        }
    }
}

int find(int x) {
    if(x!=pre[x]) pre[x]=find(pre[x]);
    return pre[x];
}

void merge(int x,int y) {
    int a=find(x);
    int b=find(y);
    if(a!=b) pre[a]=b;
}

int main() {
    cin>>a>>b>>base;

    euler_sieve(b);
    for(int i=a; i<=b; i++) {
        pre[i]=i;
    }

    for(int i=base; i<=b; i++) {
        if(!st[i]) continue;
        for(int j=i; j<=b; j+=i) {
            if(j>=a && j+i<=b) merge(j,j+i);
        }
    }

    int ans=0;
    for(int i=a; i<=b; i++) {
        if(pre[i]==i) ans++;
    }
    cout<<ans<<endl;

    return 0;
}

/*
in:10 20 3
out:7
*/





【参考文献】
https://mp.weixin.qq.com/s/XGmlFohUE1YzgP-rTbBWDw
https://blog.csdn.net/hnjzsyjyj/article/details/127017796
https://blog.csdn.net/hnjzsyjyj/article/details/125118597
https://blog.csdn.net/hnjzsyjyj/article/details/125118458





 

posted @ 2026-03-05 15:40  Triwa  阅读(19)  评论(0)    收藏  举报