洛谷題單指南-集合-P1621 集合

江城伍月發表於2024-03-26

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

相關文章