最新文章

这里会显示最新的几篇文章摘要。

集合(并查集)

P1621 集合

题目描述

Caima 给你了所有 \([a,b]\) 范围内的整数。一开始每个整数都属于各自的集合。每次你需要选择两个属于不同集合的整数,如果这两个整数拥有大于等于 \(p\) 的公共质因数,那么把它们所在的集合合并。

重复如上操作,直到没有可以合并的集合为止。

现在 Caima 想知道,最后有多少个集合。

输入格式

一行,共三个整数 \(a,b,p\),用空格隔开。

输出格式

一个数,表示最终集合的个数。

输入输出样例 #1

输入 #1

10 20 3

输出 #1

7

说明/提示

样例 1 解释

对于样例给定的数据,最后有 \(\{10,20,12,15,18\},\{13\},\{14\},\{16\},\{17\},\{19\},\{11\}\)\(7\) 个集合,所以输出应该为 \(7\)

数据规模与约定

  • 对于 \(80\%\) 的数据,\(1 \leq a \leq b \leq 10^3\)
  • 对于 \(100%\) 的数据,\(1 \leq a \leq b \leq 10^5,2 \leq p \leq b\)

分析

明显并查集,但是卡了半天(菜),第一次读错题目了,第二次,是细节没把握好,

下面看一下正常思路:首先题目要求素数,那么肯定是要筛选素数的,其次,在[a, b]看哪些是还有相同的大于p的素因子的,
这个可以用筛选素数的思路,从p开始的素数,筛选p的倍数为一个集合,
最后检查有多少的根节点就可以了

代码

#include <bits/stdc++.h>

using namespace std;

const int N = 100005;
int fa[N];
inline void init(int n) {
    for (int i = 1; i <= n; ++i) fa[i] = i;
}
int finds(int x) {
    return fa[x] == x ? x : fa[x] = finds(fa[x]);
}
inline void mergeSet(int a, int b) {
  if (finds(a) != finds(b)) fa[finds(a)] = finds(b);
  return;
}

bitset<N> pq; // pq[x] == false 表示 x 是质数,true 表示合数


int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    
    // 筛法:pq[i] = false 表示 i 暂时是质数,最终若 pq[i] = true 表示 i 为合数
    pq[0] = pq[1] = true;
    for (int i = 2; i < 20; ++i) {
      //cout << i << endl;
        if (!pq[i]) {//cout << i << endl;
            for (long long j = 1LL * i * i; j < N; j += i) {
                pq[j] = true;
        }
      }
    }
    int n,m,p;
    cin >> n >> m >> p;

    init(m);
    for (int i =p;i <= m;++i)
        if (!pq[i])
            for (int j = i+i;j <= m;j += i)
                mergeSet(i,j);

    int ans = 0;
    for (int i = n;i <= m;++i)
        if(fa[i] == i) ans++;
    cout << ans;


}
posted @ 2025-02-28 16:08  bakul  阅读(20)  评论(0)    收藏  举报