无专精|

伍六柒-

园龄:3年6个月粉丝:9关注:6

洛谷 P1621-集合

集合


题目描述

Caima 给你了所有 [a,b] 范围内的整数。一开始每个整数都属于各自的集合。每次你需要选择两个属于不同集合的整数,如果这两个整数拥有大于等于 p 的公共质因数,那么把它们所在的集合合并。
重复如上操作,直到没有可以合并的集合为止。
现在 Caima 想知道,最后有多少个集合。
(保证 1 <= a <= b <= 1e5, 2 <= p <= b)
原题链接


Input

10 20 3
1 100 2
10 100 5

Output

7
12
31

解题思路

我们可以先假想一下按这道题的要求进行操作,得到最终结果的过程。它一定是由小于等于 b 的质因数将[a,b],之间的数分成了若干堆,如果两个堆中有相同的节点,则在对这两个堆进行合并,进而得到最终结果。所以,我们的目的就明确了

  • 1 . 用质因数初次筛选
  • 2 . 根据公共元素和并堆
    举个栗子吧:
    区间[2,12],p = 2,用2筛选得到 2,4,6,8,10,12,用3筛选得到 3,6,9,12,用5筛选得到 5,10,用7筛选得到 7,用 11筛选得到 11,初步筛选到此结束。用肉眼观察的话,很容易就能看出,前三堆是可以合并到一起的,合并完之后5堆就变成了3堆,答案是3。怎么合并呢?用并查集啊,我们让每一个堆的祖宗都等于这个堆的第一个数

    得到这样的结果,在祖宗为2的堆里面,第一次出现了6,我们让6的祖宗为2,但在祖宗为3的堆中也出现了6,6的祖宗该如何变化呢?我们通过6可以直接的到它的祖宗节点2,我们再次遍历到6时,只需要把2指向3就行了,这样惊奇的发现这两个堆就合并了

    再总结一下规律可以发现,每合并一次,剩余堆数只会减少一个,所以我们可以先让ans = r-l+1,每合并一次就 ans--,最后直接输出ans即可。

AC代码

#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int p[N],l,r,q,ans; // p存节点之间的关系p[i] = j,表示 i 的祖宗是 j
bool st[N]; // 埃氏筛法,st标记被筛选过的数
void init (){
for(int i = l; i <= r; i++) //初始化,先让每个节点的祖宗等于自己
p[i] = i;
}
int find (int x){
if(p[x] != x) p[x] = find(p[x]);
return p[x];
}
void getPrime(){ //进行筛选与合并
for(int i = 2; i <= r; i++){ // i 为当前筛选使用的素数
if(!st[i]){
if(i >= q){ // 保证素数大于 p
for(int j = i + i; j <= r; j += i){
st[j] = true;
if(j-i >= l && find(j) != find(j-i))
p[find(j)] = find(j-i),ans--; // 如果j的祖宗与它前一个的不同,就让j的祖宗指向j-i的祖宗
}
}
else
for(int j = i + i; j <= r; j += i)
st[j] = true;
}
}
}
int main(){
scanf("%d %d %d",&l,&r,&q);
init();
ans = r - l + 1;
getPrime();
printf("%d",ans);
return 0;
}

本文作者:伍六柒-

本文链接:https://www.cnblogs.com/paper-plane/p/15159030.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   伍六柒-  阅读(91)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
展开