洛谷题单指南-集合-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;
}

 

posted @ 2024-03-26 09:43  五月江城  阅读(33)  评论(0编辑  收藏  举报