通过埃式筛法筛选区间素数

例题POJ2689添加链接描述
我们通过要求得一个区间[L,R]内的素数,由于L和R的数值可能会很大,如果用传统的线性筛来求解的话,会导致的问题。为了能够解决这个矛盾,我们利用效率低一点的埃氏筛来完成该题。

埃式筛的基本原理是,对于数n,必然存在2~√n之间的质数p是n的因数,所以这些质数的倍数k*p必然是合数。所以我们需要枚举筛完这个区间所需要的质数,然后枚举它的倍数然后筛去以它为因数的合数,对于该题,我们只需要筛出50000以内的质数就够了,这一步可以借用前面的线性筛来完成。

由于,如果令一个整数为k=(L-1)/p,那么k*p就是小于L的最大p的最大倍数,那么(k+1)*p就是大于等于L的第一个合数。注意,由于我们是筛的合数,故作为起始点,k+1必然是大于等于2的,否则如果k=0,p就是一个质数,就会出现错误。所以,为了保证如果p>=L,k=0时不出错,最终的起始系数x=max(2,k+1)。

我们从x开始枚举,(x+1)*p,(x+2)*p…依次标记为合数,将所有可能的p都用来筛一遍,最终留下来的就是质数了。根据本题的题意,再将合题的质数选出来即可。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#include<vector>
#define maxn 1000005
using namespace std;
typedef pair<long long,long long> P;
int check[maxn];
int prime[maxn];
int v[maxn];
int tot=0;
int fin()   //线性筛先得出所有用来筛选的质数
{
    for(int i=2;i<=50000;i++)
    {
        if(!check[i])
        {
            prime[tot]=i;
            tot++;
        }
        for(int j=0;j<tot&&prime[j]*i<=50000;j++)
        {
            check[prime[j]*i]=1;
            if(i%prime[j]==0)break;
        }
    }
}
pair<P,P> solve(long long L,long long R)
{
    memset(v,0,sizeof(v));
    for(int i=0;i<tot;i++)
    {
        for(long long j=max(2ll,(L-1)/prime[i]+1)*prime[i];j<=R;j+=prime[i])//埃氏筛筛选素数
        {
            v[j-L]=1;
        }
    }
    long long a=-1,b=-1,x=-1,y=-1,z=-1,d=maxn,f=-1;
    for(long long i=L;i<=R;i++)
    {
        if(v[i-L])continue;
        if(z==-1){z=i;continue;}
        if(i-z<d)
        {
            x=z;
            y=i;
            d=i-z;
        }
        if(i-z>f)
        {
            a=z;
            b=i;
            f=i-z;
        }
        z=i;
    }
    if(a==-1||b==-1||a==b)return make_pair(make_pair(-1,-1),make_pair(-1,-1));
    else return make_pair(make_pair(a,b),make_pair(x,y));
}
int main()
{
    long long L,R;
    fin();
    while(cin>>L>>R)
    {
        int u=0;
        if(R<2)u=1;
        L=max(2ll,L);
        pair<P,P>ans=solve(L,R);
        if(!u&&ans.first.first==-1)
        {
            cout<<"There are no adjacent primes."<<endl;
        }
        else
        {
            cout<<ans.second.first<<","<<ans.second.second<<" are closest, "<<ans.first.first<<","<<ans.first.second<<" are most distant."<<endl;
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值