LightOJ1197【数学】

引自: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 
using namespace std;
typedef long long LL;
 
const int N=1e5+10;
bool isprime[N];
vectorprime;
 
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;ib)
                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;
}








你可能感兴趣的:(素数)