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 <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; }