Min_25 sieve

Definition

From the perspective of the ideas behind this sieve, it is also called the “Extended Eratosthenes Sieve.”

Because it was invented and first used by Min\_25, it is called the “Min\_25 sieve.”

Properties

It can compute prefix sums of a class of multiplicative functions in O(n34log⁡n)O\left(\frac{n^{\frac{3}{4}}}{\log{n}}\right) or Θ(n1−ϵ)\Theta\left(n^{1 - \epsilon}\right) time.

The requirements are that f(p)f(p) is a sum of completely multiplicative functions that can be evaluated quickly as a function of pp (for example, polynomials), and that f(pc)f(p^{c}) can be evaluated quickly.

Notation

  • Unless otherwise stated, every variable denoted by pp in this section ranges over all primes.
  • x/y:=⌊xy⌋x / y := \left\lfloor\frac{x}{y}\right\rfloor
  • isprime⁡(n):=[∣{d:d∣n}∣=2]\operatorname{isprime}(n) := [ |\{d : d \mid n\}| = 2 ], namely, when nn is prime, its value is 11, and otherwise it is 00.
  • pkp_{k}: the kk-th smallest prime among all primes (for example, p1=2,p2=3p_{1} = 2, p_{2} = 3). In particular, let p0=1p_{0} = 1.
  • lpf⁡(n):=[1<n]min⁡{p:p∣n}+[1=n]\operatorname{lpf}(n) := [1 < n] \min\{p : p \mid n\} + [1 = n], that is, the smallest prime factor of nn. In particular, when n=1n=1, its value is 11.
  • Fprime(n):=∑2≤p≤nf(p)F_{\mathrm{prime}}(n) := \sum_{2 \le p \le n} f(p)
  • Fk(n):=∑i=2n[pk≤lpf⁡(i)]f(i)F_{k}(n) := \sum_{i = 2}^{n} [p_{k} \le \operatorname{lpf}(i)] f(i)

Explanation

From the definition of Fk(n)F_{k}(n), we can see that the answer is F1(n)+f(1)=F1(n)+1F_{1}(n) + f(1) = F_{1}(n) + 1.

Let us consider how to compute Fk(n)F_{k}(n). Enumerating the smallest prime factor of each ii and its exponent gives the recurrence:

Fk(n)=∑i=2n[pk≤lpf⁡(i)]f(i)=∑k≤ipi2≤n∑c≥1pic≤nf(pic)([c>1]+Fi+1(n/pic))+∑k≤ipi≤nf(pi)=∑k≤ipi2≤n∑c≥1pic≤nf(pic)([c>1]+Fi+1(n/pic))+Fprime(n)−Fprime(pk−1)=∑k≤ipi2≤n∑c≥1pic+1≤n(f(pic)Fi+1(n/pic)+f(pic+1))+Fprime(n)−Fprime(pk−1)\begin{aligned} F_{k}(n) &= \sum_{i = 2}^{n} [p_{k} \le \operatorname{lpf}(i)] f(i) \\ &= \sum_{\substack{k \le i \\ p_{i}^{2} \le n}} \sum_{\substack{c \ge 1 \\ p_{i}^{c} \le n}} f\left(p_{i}^{c}\right) ([c > 1] + F_{i + 1}\left(n / p_{i}^{c}\right)) + \sum_{\substack{k \le i \\ p_{i} \le n}} f(p_{i}) \\ &= \sum_{\substack{k \le i \\ p_{i}^{2} \le n}} \sum_{\substack{c \ge 1 \\ p_{i}^{c} \le n}} f\left(p_{i}^{c}\right) ([c > 1] + F_{i + 1}\left(n / p_{i}^{c}\right)) + F_{\mathrm{prime}}(n) - F_{\mathrm{prime}}(p_{k - 1}) \\ &= \sum_{\substack{k \le i \\ p_{i}^{2} \le n}} \sum_{\substack{c \ge 1 \\ p_{i}^{c + 1} \le n}} \left(f\left(p_{i}^{c}\right) F_{i + 1}\left(n / p_{i}^{c}\right) + f\left(p_{i}^{c + 1}\right)\right) + F_{\mathrm{prime}}(n) - F_{\mathrm{prime}}(p_{k - 1}) \end{aligned}

The last derivation is based on the following fact: when pic≤n<pic+1p_{i}^{c} \le n < p_{i}^{c + 1} holds for cc, we have pic+1>n  ⟺  n/pic<pi<pi+1p_{i}^{c + 1} > n \iff n / p_{i}^{c} < p_{i} < p_{i + 1}, so Fi+1(n/pic)=0F_{i + 1}\left(n / p_{i}^{c}\right) = 0. Its boundary value is Fk(n)=0(pk>n)F_{k}(n) = 0 (p_{k} > n).

Suppose that all Fprime(n)F_{\mathrm{prime}}(n) values have already been computed. There are two ways to compute all Fk(n)F_{k}(n) values:

  1. Compute them directly according to the recurrence.
  2. Enumerate pp from large to small and perform the transitions. The increment is nonzero only when p2<np^{2} < n, so the recurrence can be optimized with suffix sums.

Now consider how to compute Fprime(n)F_{\mathrm{prime}}{(n)}.
Looking at the process of computing Fk(n)F_{k}(n), it is easy to see that FprimeF_{\mathrm{prime}} has useful values only at the points 1,2,…,⌊n⌋,n/n,…,n/2,n1, 2, \dots, \left\lfloor\sqrt{n}\right\rfloor, n / \sqrt{n}, \dots, n / 2, n, of which there are O(n)O(\sqrt{n}).
In general, f(p)f(p) is a low-degree polynomial in pp, which can be written as f(p)=∑aipcif(p) = \sum a_{i} p^{c_{i}}.
Thus, the contribution of each pcip^{c_{i}} to Fprime(n)F_{\mathrm{prime}}(n) is ai∑2≤p≤npcia_{i} \sum_{2 \le p \le n} p^{c_{i}}.
Considering the contribution of each pcip^{c_{i}} separately, the problem becomes: given n,s,g(p)=psn, s, g(p) = p^{s}, for every m=n/im = n / i, find ∑p≤mg(p)\sum_{p \le m} g(p).

Note

g(p)=psg(p) = p^{s} is a completely multiplicative function!

Now define Gk(n):=∑i=2n[pk<lpf⁡(i)∨isprime⁡(i)]g(i)G_{k}(n) := \sum_{i = 2}^{n} \left[p_{k} < \operatorname{lpf}(i) \lor \operatorname{isprime}(i)\right] g(i), namely, after the kk-th round of the Eratosthenes sieve, the sum of the gg values of the numbers that remain.
For a composite number x≤nx \le n, we necessarily have lpf⁡(x)≤x≤n\operatorname{lpf}(x) \le \sqrt{x} \le \sqrt{n}. Let pℓ(n)p_{\ell(n)} be the largest prime not exceeding n\sqrt{n}. Then ∑2≤p≤ng(p)=Gℓ(n)(n)\sum_{2\le p\le n}g(p) = G_{\ell(n)}(n), meaning that after ℓ\ell rounds of the Eratosthenes sieve, only primes remain. Considering the boundary value of GG, it is clearly G0(n)=∑i=2ng(i)G_{0}(n) = \sum_{i = 2}^{n} g(i). (Recall that we specially defined p0=1p_{0} = 1.)
For the transition, consider the Eratosthenes sieve process and separate the contributions of each part:

  1. For the part with n<pk2n < p_{k}^{2}, the value of GG does not change, so Gk(n)=Gk−1(n)G_{k}(n) = G_{k - 1}(n).
  2. For the part with pk2≤np_{k}^{2} \le n, every removed number has pkp_{k} as a prime factor, contributing −g(pk)Gk−1(n/pk)-g(p_{k}) G_{k - 1}(n / p_{k}).
  3. In the second part, because pk2≤n  ⟺  pk≤n/pkp_{k}^{2} \le n \iff p_{k} \le n / p_{k}, the condition lpf⁡(i)<pk\operatorname{lpf}(i) < p_{k} is met by numbers ii. This part must be added back, giving g(pk)Gk−1(pk−1)g(p_{k}) G_{k - 1}(p_{k - 1}).

Thus,

Gk(n)=Gk−1(n)−[pk2≤n]g(pk)(Gk−1(n/pk)−Gk−1(pk−1))G_{k}(n) = G_{k - 1}(n) - \left[p_{k}^{2} \le n\right] g(p_{k}) (G_{k - 1}(n / p_{k}) - G_{k - 1}(p_{k - 1}))

Complexity analysis

For computing Fk(n)F_{k}(n), the time complexity of the first method has been proved to be O(n1−ϵ)O\left(n^{1 - \epsilon}\right) (see Section 2.3 of Zhen-Ting Zhu's training-team paper 《Some Special Summation Problems for Number-Theoretic Functions》);
for the second method, its essence is the second part of the Du sieve, which is also mentioned in Ren Zhizhou's paper 《Several Methods for Summing Multiplicative Functions》 (Section 6.5.4), and its time complexity has been proved to be O(n34log⁡n)O\left(\frac{n^{\frac{3}{4}}}{\log{n}}\right).

For computing Fprime(n)F_{\mathrm{prime}}(n), the implementation is in fact the same as the first part of the Du sieve.
For each m=n/im = n / i, only enumerating a value satisfying pk2≤mp_{k}^{2} \le m and then processing the prime pkp_{k} contributes to the time complexity, so the time complexity can be estimated as follows:

T(n)=∑i2≤nO(π(i))+∑i2≤nO(π(ni))=∑i2≤nO(iln⁡i)+∑i2≤nO(niln⁡ni)=O(∫1nnxlog⁡nxdx)=O(n34log⁡n)\begin{aligned} T(n) &= \sum_{i^{2} \le n} O\left(\pi\left(\sqrt{i}\right)\right) + \sum_{i^{2} \le n} O\left(\pi\left(\sqrt{\frac{n}{i}}\right)\right) \\ &= \sum_{i^{2} \le n} O\left(\frac{\sqrt{i}}{\ln{\sqrt{i}}}\right) + \sum_{i^{2} \le n} O\left(\frac{\sqrt{\frac{n}{i}}}{\ln{\sqrt{\frac{n}{i}}}}\right) \\ &= O\left(\int_{1}^{\sqrt{n}} \frac{\sqrt{\frac{n}{x}}}{\log{\sqrt{\frac{n}{x}}}} \mathrm{d} x\right) \\ &= O\left(\frac{n^{\frac{3}{4}}}{\log{n}}\right) \end{aligned}

For the space complexity, observe that both FkF_{k} and FprimeF_{\mathrm{prime}} take effective values only at n/in / i, for a total of O(n)O(\sqrt{n}) points. Recording only the effective values reduces the space complexity to O(n)O(\sqrt{n}).

First, a single number-theoretic block decomposition gives all effective values; record them in an array of size O(n)O(\sqrt{n}) called lis\text{lis}. For an effective value vv, let id(v)\text{id}(v) be the index of vv in lis\text{lis}. It is easy to see that for every effective value vv, id(v)≤n\text{id}(v) \le \sqrt{n}.

Then consider separately effective values no greater than n\sqrt{n} and those greater than n\sqrt{n}: for effective values no greater than n\sqrt{n} with value vv, use an array le\text{le} to record its id(v)\text{id}(v), namely lev=id(v)\text{le}_v = \text{id}(v); for effective values greater than n\sqrt{n} with value vv, use an array ge\text{ge} to record id(v)\text{id}(v). Since vv is too large, use v′=n/v<nv' = n / v < \sqrt{n} to record id(v)\text{id}(v), namely gev′=id(v)\text{ge}_{v'} = \text{id}(v).

Thus, two arrays of size O(n)O(\sqrt{n}) can record the id\text{id} of every effective value and support O(1)O(1) queries. When computing FkF_{k} or FprimeF_{\mathrm{prime}}, use the id\text{id} of an effective value as the index instead of the value itself, thereby reducing the space complexity to O(n)O(\sqrt{n}).

Procedure

For computing Fk(n)F_{k}(n), in implementations we generally choose the first method because it is easier to implement and often performs better than the second method when the data size is small.

For computing Fprime(n)F_{\mathrm{prime}}(n), implement the recurrence directly.

For pk2≤np_{k}^{2} \le n, a linear sieve can preprocess sk:=Fprime(pk)s_{k} := F_{\mathrm{prime}}(p_{k}) to replace FkF_{k} in the recurrence for Fprime(pk−1)F_{\mathrm{prime}}(p_{k - 1}).
Correspondingly, in the recurrence for GG, the value Gk−1(pk−1)=∑i=1k−1g(pi)G_{k - 1}(p_{k - 1}) = \sum_{i = 1}^{k - 1} g(p_{i}) can also be preprocessed in this way.

When using the Extended Eratosthenes Sieve to compute the prefix sum of a multiplicative function ff, the following points must be clear:

  • How to quickly sieve out (generally in linear time) the first n\sqrt{n} values of ff;
  • The polynomial representation of f(p)f(p);
  • How to quickly compute f(pc)f(p^{c}).

After clarifying the points above, implement the following parts in order:

  1. Sieve the primes in [1,n][1, \sqrt{n}] and the first n\sqrt{n} values of ff;
  2. For every term in the polynomial representation of f(p)f(p), sieve out the corresponding GG and combine them to obtain all useful values of FprimeF_{\mathrm{prime}} numbered O(n)O(\sqrt{n});
  3. Implement the recurrence for FkF_{k} recursively to obtain F1(n)F_{1}(n).

Examples

Luogu P4213 [Template] Du sieve

Find ∑i=1nφ(i)\displaystyle \sum_{i = 1}^{n} \varphi(i) and ∑i=1nμ(i)\displaystyle \sum_{i = 1}^{n} \mu(i).

Solution

To compute the prefix sum of φ(i)\varphi(i), we first know that f(p)=p−1f(p) = p - 1. For the linear term f(p)f(p) of (p)(p), we have g(p)=p,G0(n)=∑i=2ng(i)=(n+2)(n−1)2g(p) = p, G_{0}(n) = \sum_{i = 2}^{n} g(i) = \frac{(n + 2) (n - 1)}{2}; for the constant term f(p)f(p) of (−1)(-1), we have g(p)=−1,G0(n)=∑i=2ng(i)=−n+1g(p) = -1, G_{0}(n) = \sum_{i = 2}^{n} g(i) = -n + 1. Sieve twice and add the results to obtain all FprimeF_{\mathrm{prime}} values at the required O(n)O(\sqrt{n}) points.

To compute the prefix sum of μ(i)\mu(i), we know that f(p)=−1f(p) = -1. Thus g(p)=−1,G0(n)=∑i=2ng(i)=−n+1g(p) = -1, G_{0}(n) = \sum_{i = 2}^{n} g(i) = -n + 1. A single sieve gives all FprimeF_{\mathrm{prime}} values at the required O(n)O(\sqrt{n}) points.

LOJ 6053 Simple Function

Given f(n)f(n):

f(n)={1n=1pxor⁡cn=pcf(a)f(b)n=ab∧a⊥bf(n) = \begin{cases} 1 & n = 1 \\ p \operatorname{xor} c & n = p^{c} \\ f(a)f(b) & n = ab \land a \perp b \end{cases}

Find ∑i=1nf(i)\displaystyle \sum_{i = 1}^{n} f(i).

Solution

It is easy to see that f(p)=p−1+2[p=2]f(p) = p - 1 + 2[p = 2]. Thus, sieve it in the same way as φ\varphi, with a separate case for 22.

Reference code
/* "LOJ #6053" Simple Function */
#include <cmath>
#include <iostream>

constexpr int MAXS = 200000;  // 2sqrt(n)
constexpr int mod = 1000000007;

template <typename x_t, typename y_t>
void inc(x_t &x, const y_t &y) {
  x += y;
  (mod <= x) && (x -= mod);
}

template <typename x_t, typename y_t>
void dec(x_t &x, const y_t &y) {
  x -= y;
  (x < 0) && (x += mod);
}

template <typename x_t, typename y_t>
int sum(const x_t &x, const y_t &y) {
  return x + y < mod ? x + y : (x + y - mod);
}

template <typename x_t, typename y_t>
int sub(const x_t &x, const y_t &y) {
  return x < y ? x - y + mod : (x - y);
}

template <typename _Tp>
int div2(const _Tp &x) {
  return ((x & 1) ? x + mod : x) >> 1;
}

// all of the above are to avoid negative numbers and handle modular reduction
template <typename _Tp>
long long sqrll(const _Tp &x) {  // square function
  return (long long)x * x;
}

int pri[MAXS / 7], lpf[MAXS + 1], spri[MAXS + 1], pcnt;

void sieve(const int &n) {
  for (int i = 2; i <= n; ++i) {
    if (lpf[i] == 0) {  // record the prime
      lpf[i] = ++pcnt;
      pri[lpf[i]] = i;
      spri[pcnt] = sum(spri[pcnt - 1], i);  // prefix sum
    }
    for (int j = 1, v; j <= lpf[i] && (v = i * pri[j]) <= n; ++j) lpf[v] = j;
  }
}

long long global_n;
int lim;
int le[MAXS + 1],  // x <= \sqrt{n}
    ge[MAXS + 1];  // x > \sqrt{n}
#define idx(v) (v <= lim ? le[v] : ge[global_n / v])

int G[MAXS + 1][2], Fprime[MAXS + 1];
long long lis[MAXS + 1];
int cnt;

void init(const long long &n) {
  for (long long i = 1, j, v; i <= n; i = n / j + 1) {
    j = n / i;
    v = j % mod;
    lis[++cnt] = j;
    (j <= lim ? le[j] : ge[global_n / j]) = cnt;
    G[cnt][0] = sub(v, 1ll);
    G[cnt][1] = div2((long long)(v + 2ll) * (v - 1ll) % mod);
  }
}

void calcFprime() {
  for (int k = 1; k <= pcnt; ++k) {
    const int p = pri[k];
    const long long sqrp = sqrll(p);
    for (int i = 1; lis[i] >= sqrp; ++i) {
      const long long v = lis[i] / p;
      const int id = idx(v);
      dec(G[i][0], sub(G[id][0], k - 1));
      dec(G[i][1], (long long)p * sub(G[id][1], spri[k - 1]) % mod);
    }
  }
  /* F_prime = G_1 - G_0 */
  for (int i = 1; i <= cnt; ++i) Fprime[i] = sub(G[i][1], G[i][0]);
}

int f_p(const int &p, const int &c) {
  /* f(p^{c}) = p xor c */
  return p ^ c;
}

int F(const int &k, const long long &n) {
  if (n < pri[k] || n <= 1) return 0;
  const int id = idx(n);
  long long ans = Fprime[id] - (spri[k - 1] - (k - 1));
  if (k == 1) ans += 2;
  for (int i = k; i <= pcnt && sqrll(pri[i]) <= n; ++i) {
    long long pw = pri[i], pw2 = sqrll(pw);
    for (int c = 1; pw2 <= n; ++c, pw = pw2, pw2 *= pri[i])
      ans +=
          ((long long)f_p(pri[i], c) * F(i + 1, n / pw) + f_p(pri[i], c + 1)) %
          mod;
  }
  return ans % mod;
}

using std::cin;
using std::cout;

int main() {
  cin.tie(nullptr)->sync_with_stdio(false);
  cin >> global_n;
  lim = sqrt(global_n);  // upper bound

  sieve(lim + 1000);  // preprocess
  init(global_n);
  calcFprime();
  cout << (F(1, global_n) + 1ll + mod) % mod << '\n';

  return 0;
}

This content comes from OI Wiki: Min_25 sieve. See the original for authors and revision history. AIOJ has adapted the formatting, in-site links, and resource paths.

Content is shared under CC BY-SA 4.0 and SATA license; additional terms are governed by the original notice. Thanks to the OI Wiki authors and contributors; per the SATA terms, please visit the OI Wiki repository and star it to show your support.