P1621 集合
题目描述
现在给你一些连续的整数,它们是从A到B的整数。一开始每个整数都属于各自的集合,然后你需要进行一下的操作:
每次选择两个属于不同集合的整数,如果这两个整数拥有大于等于P的公共质因数,那么把它们所在的集合合并。
反复如上操作,直到没有可以合并的集合为止。
现在Caima想知道,最后有多少个集合。
输入输出格式
输入格式:
一行,三个整数A,B,P。
【数据规模】
A≤B≤100000;
2≤P≤B。
输出格式:
一个数,表示最终集合的个数。
输入输出样例
说明
有80%的数据B≤1000。
样例解释{10,20,12,15,18},{13},{14},{16},{17},{19},{11}。
Solution:
本题比较水。
像这种集合合并,然后统计联通块的题目,不难想到用并查集。
然后我们就直接线筛出$b$以内的素数,然后二分出不小于$p$的素数的位置,然后在$[a,b]$范围内枚举每个符合条件的质数的倍数,然后用并查集合并。
最后查询时,再维护一下树的信息,统计一下联通块个数就好了。
代码:
#include<bits/stdc++.h> #define il inline #define ll long long #define For(i,a,b) for(int (i)=(a);(i)<=(b);(i)++) #define Bor(i,a,b) for(int (i)=(b);(i)>=(a);(i)--) #define Max(a,b) ((a)>(b)?(a):(b)) #define Min(a,b) ((a)>(b)?(b):(a)) using namespace std; const int N=100005; int prime[N+5],cnt,a,b,p,fa[N]; bool isprime[N+5],vis[N]; il int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);} il void init(){ For(i,2,b){ if(!isprime[i]) prime[++cnt]=i; for(int j=1;j<=cnt&&prime[j]*i<=b;j++){ isprime[prime[j]*i]=1; if(i%prime[j]==0)break; } } } int main(){ ios::sync_with_stdio(0); cin>>a>>b>>p; For(i,a,b) fa[i]=i; init(); int k=lower_bound(prime+1,prime+cnt+1,p)-prime; For(i,k,cnt){ int l=ceil(a*1.0/prime[i]),r=b/prime[i]; int x=find(fa[l*prime[i]]); For(j,l+1,r) { int y=find(fa[j*prime[i]]); if(y!=x)fa[y]=x; } } int tot=0; For(i,a,b) { fa[i]=find(fa[i]); if(!vis[fa[i]]) vis[fa[i]]=1,tot++; } cout<<tot; return 0; }
PS:~蒟蒻写博客不易,转载请注明出处,万分感谢!~