关于素数筛我们介绍常用的两种素数筛:
普通筛法求素数:复杂度为O(nloglogn)
1 const int maxn = 100000; 2 bool vis[maxn]; 3 memset(vis, false, sizeof(vis)); 4 vis[1] = true; 5 for(int i = 2; i < maxn; ++i) if(!vis[i]){ 6 for(int j = 2*i; j < maxn; j += i) 7 vis[j] = true; 8 } 9 //对于一个数n,若vis[n] == false 则n为素数,否则为合数
以上素数筛可以优化一下:
1 const int maxn = 100000; 2 bool vis[maxn]; 3 memset(vis, false, sizeof(vis)); 4 vis[1] = true; 5 int m = sqrt(maxn+0.5); 6 for(int i = 2; i <= m; ++i) if(!vis[i]) 7 for(int j = i*i; j < maxn; j+=i) 8 vis[j] = true; 9 // 同样的,若vis[n] == false,则n为素数
欧拉筛(线性筛):复杂度为O(n)
1 const int maxn = 100000; 2 int primer[maxn], n = 0; 3 bool vis[maxn]; 4 memset(vis, false, sizeof(vis)); 5 vis[1] = true; 6 for(int i = 2; i < maxn; ++i) { 7 if(!vis[i]) { 8 primer[n++] = i; 9 } 10 for(int j = 0; j < n; ++j) { 11 if(i * primer[j] >= maxn) { 12 break; 13 } 14 vis[i*primer[j]] = true; 15 if(i % primer[j] == 0) { 16 break; 17 } 18 } 19 }
上面这个算法保证了每个合数只会被它的最小质因子给筛去,所以算法复杂度就是O(n)
区间素数筛:
LightOJ - 1197 : https://vjudge.net/problem/LightOJ-1197
如果我们要统计区间[a, b], (1 <= a <= b <= 2^31) 之间的素数的个数,由于a,b的范围很大,但是b-a <= 100000,所以我们可以遍历这个区间内的所有数,判断它们是不是素数。判断一个数是不是素数,可以用O(√n)的算法,但是在这题行不通,我们可以借鉴素数筛的思想来解决这题。对于在区间[a,b]内的合数,它们的最小质因子一定是小于等于sqrt(b)的,所以我们可以先把1到sqrt(b)的所有素数求出来,再利用这些素数去筛掉[a,b]之间的合数。具体操作如下:
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 typedef long long LL; 5 const int maxn = 70000; 6 const int maxd = 100010; 7 bool vis[maxn]; 8 bool primes[maxd]; 9 10 // 打出1到2^16之间的素数 11 void init() 12 { 13 for(LL i = 2; i < maxn; ++i)if(!vis[i]) 14 for(LL j = i*i; j < maxn; j+=i) 15 vis[j] = true; 16 } 17 18 LL get_primes(LL a, LL b) 19 { 20 memset(primes, false, sizeof(primes)); 21 if(a == 1) primes[0] = true; 22 LL m = sqrt(b+0.5); 23 for(LL i = 2; i <= m; ++i) if(!vis[i]) 24 { 25 for(LL j = max(2LL, (a+i-1)/i)*i; j <= b; j+=i) 26 primes[j-a] = true; 27 } 28 LL ans = 0; 29 for(LL i = a; i <= b; ++i) 30 if(!primes[i-a]) ans++; 31 return ans; 32 } 33 34 int main() 35 { 36 init(); 37 int T, ca = 1; 38 scanf("%d", &T); 39 while(T--) 40 { 41 LL a, b; 42 scanf("%lld%lld", &a, &b); 43 LL ans = get_primes(a, b); 44 printf("Case %d: %lld\n", ca++, ans); 45 } 46 return 0; 47 }