P1621 集合

题目描述

现在给你一些连续的整数,它们是从A到B的整数。一开始每个整数都属于各自的集合,然后你需要进行一下的操作:

每次选择两个属于不同集合的整数,如果这两个整数拥有大于等于P的公共质因数,那么把它们所在的集合合并。

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

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

输入输出格式

输入格式:

 

一行,三个整数A,B,P。

【数据规模】

A≤B≤100000;

2≤P≤B。

 

输出格式:

 

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

 

输入输出样例

输入样例#1: 
10 20 3
输出样例#1: 
7

说明

有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;
}
原文地址:https://www.cnblogs.com/five20/p/9236195.html