埃氏筛法的一般写法(区间筛法)
问题
求 $[L, R]$ 之间的素数表。
解法
一个合数 $n$ 的最小素因子不超过 $\sqrt{n}$。因此,在埃氏筛法的过程中,每当“新发现”一个素数 $p$,可以从 $p^2$ 筛起,而不必从 $2p$ 筛起。
先用埃氏筛法求出 $[1,\lfloor \sqrt{R} \rfloor]$ 上的素数表,再在 $[L, R]$ 上用埃氏筛法求素数。
const int N(1e5); bool isprime[N]; int prime[N]; void init() { memset(isprime, -1, sizeof isprime); isprime[0] = isprime[1] = 0; int np = 0; for (int i = 0; i < N; i++) { if (isprime[i]) { prime[np++] = i; for (int j = i * i; j < N; j += i) isprime[j] = 0; } } } typedef long long ll; const int M(1e5); bool ok[M]; int res[M]; int seive(ll l, ll r) { assert(1 <= l && l <= r); memset(ok, -1, sizeof ok); if (l == 1) ok[0] = 0; //error-prone int k = sqrt(r); for (int i = 0; prime[i] <= k; i++) { ll j = (l + prime[i] - 1) / prime[i] * prime[i]; j = max(j, (ll) prime[i] * prime[i]); for (; j <= r; j += prime[i]) ok[j - l] = 0; } int np = 0; for (int i = 0; i <= r - l; i++) if (ok[i]) res[np++] = i + (ll) l; return np; }
更新:
不必先把 $[2, \lfloor \sqrt{R} \rfloor]$ 上的素数存下来。更好的做法是先分别做好 $[2, \lfloor \sqrt{R} \rfloor]$ 和 $[L, R]$ 的表,然后从 $[2, \lfloor \sqrt{R} \rfloor]$ 的表中筛得素数的同时,也将其倍数从 $[L, R]$ 中划去。
const int N = 1e6 + 5, M = sqrt(1e9); bool is_prime[N]; // 注意:用 sqrt(1e9) + 1 作为数组大小不符合标准, sqrt(1e9) 不是编译期常量,但是 gcc 支持这样做。 bool is_prime_small[M + 1]; void segment_seive(int l, int r) { // [l, r] assert(1 <= l && l <= r); int t = sqrt(r + 0.5); for (int i = 2; i <= t; i++) is_prime_small[i] = true; for (int i = 0; i <= r - l; i++) is_prime[i] = true; if (l == 1) is_prime[0] = false; for (int i = 2; i <= t; i++) if (is_prime_small[i]) { for (int j = i * i; j <= t; j += i) is_prime_small[j] = false; for (int j = max(i * i, (l + i - 1) / i * i); j <= r; j += i) is_prime[j - l] = false; } }