LightOJ1197【数学】

来源:互联网 发布:钱龙分析软件 编辑:程序博客网 时间:2024/05/18 05:49

引自:WONTER

题意:

给你两个数,a,b,让你求区间[a,b]里面有多少个素数;

思路:

首先要知道,我们要想筛 [1, b] 中所有的素数,只需要用到 [1, sqrt(b)] 中的所有素数来筛就可以了。

假设我们是求 [1, b] 中所有的素数,我们就只需要打表出 [1, sqrt(b)] 的素数,然后用这些素数直接去套用常规的素数筛选方法就好了,也就是(j = prime[i] * 2; j <= b; j += prime[i]) isPrime[j] = false,但由于 b < 2^31 数据太大,我们不能直接开这么大的空间,就算能开,一个一个的置 false 也会 TLE

由于 a 和 b 的范围太大,直接素数筛肯定不可以的,但注意到 b - a <= 100000,所以可以利用这一点,减少空间的使用。所以 j 就从第一个大于 a 的 prime[i] 的倍数开始,其他的不变,并且我们置为 false 的时候也不是置isPrime[j] = false,因为这个 j 会很大,我们把 j 离散化,置isPrime[j - a] = false即可,最后统计 [0, b - a] 中有多少个 isPrime[j]是 true 就可以了

但要特判 a 为 1 的时候,1 也被算成素数了,这个时候要减去。

这里有一个小小的CASE就是:

求>=a的最小b倍;

①:大哥的写法:(a+b-1)/b*b;没有严格证明。。

②:队友写法:a+b-a%b,但还要判断是不是a%b!=0;

#include <bits/stdc++.h>using namespace std;typedef long long LL; const int N=1e5+10;bool isprime[N];vector<int>prime; void init(){    prime.clear();    for(int i=1;i<=55000;i++)    {        if(i&1)            isprime[i]=true;        else isprime[i]=false;    }    isprime[2]=1;    for(int i=2;i<=55000;i++)    {        if(isprime[i])        {            prime.push_back(i);            for(int j=i+i;j<=55000;j+=i)                isprime[j]=false;        }    }} int main(){    init();    int a,b;    int T,cas=1;    scanf("%d",&T);    while(T--)    {        scanf("%d%d",&a,&b);        memset(isprime,true,sizeof(isprime));        for(int i=0;i<prime.size();i++)        {            if(1ll*prime[i]*prime[i]>b)                break;            LL j;            if(a/prime[i]<2)                j=prime[i]+prime[i];            else                j=((1ll*a-1)/prime[i]+1)*prime[i];            while(j<=b)            {                isprime[j-a]=false;                j+=prime[i];            }        }        int num=b-a;        int ans=0;        for(int i=0;i<=num;i++)        {            if(isprime[i])                ans++;        }        if(a==1)            ans--;        printf("Case %d: %d\n",cas++,ans);    }    return 0;}








0 0
原创粉丝点击