poj 2689 Prime Distance 素数

题意:输入区间[l,u],其中l和u为int范围的整数,区间最大为1000000。求出[l,u]中,相邻素数只差最大和最小的素数对。当存在多个时,输出较小的素数对。

题解:l,u范围太大,不能直接求int范围的素数。而区间间隔比较小,只有1e6,而且对于int范围内的合数来说,最小质因子必定小于2^16。所以可以求出[l,u]中合数,转而求出素数,然后暴力枚举所有素数对即可。

如何求区间[l,u]中的合数:上面已经说了,合数的最小质因子小于2^16,即小于50000。所以先求出小于50000的所有素数。则区间[l,u]中的合数,必定可以表示为小于50000的素数的倍数。对于素数p来说,令a=(l-1)/p+1,b=u/p。则枚举j=a到b,j*p可以枚举所有[l,u]中质因子含有p的合数。枚举所有小于50000的素数,然后用上述方式枚举倍数,即可找出[l,u]中所有的合数。

由于l,u在int范围,所以不能直接用数组标记。需要加个偏移量,取l,则数组大小小于1e6的f[0,u-l],即可标记。

接着枚举区间中所有的相邻素数对即可。

特别注意:由于1不是小于50000的素数的倍数,所以在与合数相斥中,会被当成素数。需要特别处理下。


耗时:79MS

#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <iostream>
#include <algorithm>
using namespace std;
const int INF=1e9;
const int maxn=5e4+10;
const int maxm=1e6+10;
int l,u,prime[maxn],vis[maxn],f[maxm],t;
void init()//求出50000内的素数,int范围内的合数最小质因子必定小于2^16。
{
    int i,j,k,m;
    t=0;
    m=(int)sqrt(maxn+0.5);
    memset(vis,0,sizeof(vis));
    for(i=2;i<=m;i++)
    {
        if(!vis[i])
        {
            for(j=i*i;j<maxn;j+=i)
            vis[j]=1;
        }
    }
    for(i=2;i<maxn;i++)if(!vis[i])prime[t++]=i;
}
int main()
{
    init();
    while(cin>>l>>u)
    {
        if(l==1)l=2;//注意l=1会出问题。
        int i,j,k,a,b;
        memset(f,0,sizeof(f));
        for(i=0;i<t;i++)
        {
            a=(l-1)/prime[i]+1;
            b=u/prime[i];
            for(j=a;j<=b;j++)if(j>1)f[j*prime[i]-l]=1;//[l,u]区间小于1e6,而l,u数值范围为int,所以偏移l们就能用数组存了
        }
        int p=-1,max_ans=-1,min_ans=INF,x1,y1,x2,y2;
        for(i=0;i<=u-l;i++)//暴力枚举。。
        {
            if(f[i]==0)
            {
                if(p==-1){p=i;continue;}
                if(max_ans<i-p){max_ans=i-p;x1=p+l;y1=i+l;}
                if(min_ans>i-p){min_ans=i-p;x2=p+l;y2=i+l;}
                p=i;
            }
        }
        if(max_ans==-1)cout<<"There are no adjacent primes."<<endl;
        else cout<<x2<<","<<y2<<" are closest, "<<x1<<","<<y1<<" are most distant."<<endl;
    }
    return 0;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值