素数计数问题
# 素数计数问题
给定范围,求该范围内素数个数。
# 例题1 洛谷P3192
求 中素数的个数。()
# 例题2 HDU 5901
求中素数的个数。()
# 朴素方法--埃拉托色尼筛
很容易想到埃拉托色尼筛(Eratosthenes Sieve),实现起来也很简单:先预处理出小于的所有质数,接着按照埃拉托色尼筛的方法算一遍即可,缺点是耗时长,复杂度
PS. 过例题还是绰绰有余的
# Lucy_Hedgehog 解法
这类方法的问题在于递归层数过多,容易爆栈
# 引入: Project Euler第10题
题面: Find the sum of all the primes below two million.
题意:求小于两百万的质数之和
# 解法
定义为中筛到时,剩下的数的总和,即:
$S(n,p)=\sum{v\mid v\text{为质数 或 } v\text{的最小质因数大于}p} $
接下来我们分类讨论
,显然有
不是素数,因为在之前的过程中已经被别的数筛掉,因而这一步什么也不做,
是素数,但是,此时内不存在以为因数的合数,因此
是素数,且
下面着重讨论第4种情况
# 代码
def P10(n):
r = int(n**0.5)
assert r*r <= n and (r+1)**2 > n
V = [n//i for i in range(1,r+1)]
V += list(range(V[-1]-1,0,-1))
S = {i:i*(i+1)//2-1 for i in V}
for p in range(2,r+1):
if S[p] > S[p-1]: # p is prime
sp = S[p-1] # sum of primes smaller than p
p2 = p*p
for v in V:
if v < p2: break
S[v] -= p*(S[v//p] - sp)
return S[n]
2
3
4
5
6
7
8
9
10
11
12
13
14
原作者:"The complexity of this algorithm is about and needs 9 ms to find the solution. "
# 稍加变通
令为中筛到的倍数时,剩下的数的总数(此时的倍数已被筛掉)。
可以用公式来表达
对于,计算方式如下
解释一下到的转换:的含义为在内,质数和最小质因数大于的合数的个数,因此要减去小于的质数的个数
# 例题1 AC代码 用时: 338ms
用记忆化搜索应该会更快,当然也可以在递归时直接跳转到下一个质数(而不是-1)
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e4 + 100;
int prime[maxn], prime_cnt = 0;
bool is_prime[maxn];
void getPrime(){ // 线性筛筛出小于sqrt(n)的质数
memset(is_prime, true, sizeof is_prime);
for(int i=2; i<maxn; i++){
if(is_prime[i]) prime[prime_cnt++] = i;
for(int j=0; j<prime_cnt && prime[j] * i < maxn; j++){
is_prime[prime[j] * i] = false;
if(i % prime[j] == 0) break;
}
}
}
int prime_count(int n, int m){
if(m <= 1) return n-1;
if(!is_prime[m] || m * m > n) return prime_count(n, m-1);
return prime_count(n, m-1) - prime_count(n/m, m-1) + prime_count(m-1, sqrt(m-1));
}
signed main(){
ios::sync_with_stdio(false), cin.tie(NULL);
getPrime();
int n; cin >> n;
cout << prime_count(n, sqrt(n)) << endl;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 该方法在例题2中TLE
# 黑科技(不知道叫什么)
疑似Mapes
据说复杂度
HDU-5901 AC代码(1419ms, 6.4MB)
#include <bits/stdc++.h>
#define ll long long
using namespace std;
// f[i] means pi(n/i), g[i] means pi(i)
ll f[340000], g[340000], n;
void init() {
ll i, j, m;
for (m = 1; m * m <= n; ++m) f[m] = n / m - 1;
for (i = 1; i <= m; ++i) g[i] = i - 1;
for (i = 2; i <= m; ++i) {
if (g[i] == g[i - 1]) continue;
for (j = 1; j <= min(m - 1, n / i / i); ++j) {
if (i * j < m) f[j] -= f[i * j] - g[i - 1];
else f[j] -= g[n / i / j] - g[i - 1];
}
for (j = m; j >= i * i; --j)
g[j] -= g[j / i] - g[i - 1];
}
}
signed main() {
ios::sync_with_stdio(false), cin.tie(NULL);
while (cin >> n) {
init();
cout << f[1] << '\n';
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# Meissel-Lehmer 算法
吐槽:事实证明,百度搜不到的时候,谷歌一下能省不少时间(尤其是技术类问题)。两个原因:1. csdn 2. 百度不收录github.io
模板(HDU 5901)(234ms, 43.3MB)
求内素数个数
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 5e6 + 2; // 非n^(1/3) 亦非n^(2/3)
bool np[N];
int prime[N], pi[N];
int getprime() {
int cnt = 0;
np[0] = np[1] = true;
pi[0] = pi[1] = 0;
for (int i = 2; i < N; ++i) {
if (!np[i]) prime[++cnt] = i;
pi[i] = cnt;
for (int j = 1; j <= cnt && i * prime[j] < N; ++j) {
np[i * prime[j]] = true;
if (i % prime[j] == 0) break;
}
}
return cnt;
}
const int M = 7;
const int PM = 2 * 3 * 5 * 7 * 11 * 13 * 17;
int phi[PM + 1][M + 1], sz[M + 1];
void init() {
getprime();
sz[0] = 1;
for (int i = 0; i <= PM; ++i)
phi[i][0] = i;
for (int i = 1; i <= M; ++i) {
sz[i] = prime[i] * sz[i - 1];
for (int j = 1; j <= PM; ++j)
phi[j][i] = phi[j][i - 1] - phi[j / prime[i]][i - 1];
}
}
int sqrt2(ll x) {
ll r = (ll)sqrt(x - 0.1);
while (r * r <= x)
++r;
return int(r - 1);
}
int sqrt3(ll x) {
ll r = (ll)cbrt(x - 0.1);
while (r * r * r <= x)
++r;
return int(r - 1);
}
ll getphi(ll x, int s) {
if (s == 0) return x;
if (s <= M) return phi[x % sz[s]][s] + (x / sz[s]) * phi[sz[s]][s];
if (x <= prime[s] * prime[s]) return pi[x] - s + 1;
if (x <= prime[s] * prime[s] * prime[s] && x < N) {
int s2x = pi[sqrt2(x)];
ll ans = pi[x] - (s2x + s - 2) * (s2x - s + 1) / 2;
for (int i = s + 1; i <= s2x; ++i)
ans += pi[x / prime[i]];
return ans;
}
return getphi(x, s - 1) - getphi(x / prime[s], s - 1);
}
ll getpi(ll x) {
if (x < N) return pi[x];
ll ans = getphi(x, pi[sqrt3(x)]) + pi[sqrt3(x)] - 1;
for (int i = pi[sqrt3(x)] + 1, ed = pi[sqrt2(x)]; i <= ed; ++i)
ans -= getpi(x / prime[i]) - i + 1;
return ans;
}
ll lehmer_pi(ll x) { // 与getpi功能一样,但似乎比较慢
if (x < N) return pi[x];
int a = (int)lehmer_pi(sqrt2(sqrt2(x)));
int b = (int)lehmer_pi(sqrt2(x));
int c = (int)lehmer_pi(sqrt3(x));
ll sum = getphi(x, a) + (ll)(b + a - 2) * (b - a + 1) / 2;
for (int i = a + 1; i <= b; i++) {
ll w = x / prime[i];
sum -= lehmer_pi(w);
if (i > c) continue;
ll lim = lehmer_pi(sqrt2(w));
for (int j = i; j <= lim; j++)
sum -= lehmer_pi(w / prime[j]) - (j - 1);
}
return sum;
}
int main() {
ios::sync_with_stdio(false), cin.tie(NULL);
init();
ll n;
while (cin >> n) {
cout << getpi(n) << '\n';
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# 参考
https://www.zhihu.com/question/29580448 菜鱼ftfish 的回答
https://www.luogu.com.cn/problem/solution/P3912 吾乃会虎 的题解