Prefix sums of multiplicative or prime-supported functions for up to –, all built on the floor-value set of size (Floor Sums).
| Method | Computes | Time | Space |
|---|---|---|---|
| Lucy (Legendre / Meissel style) | , | ||
| Min_25 | , multiplicative with a low-degree polynomial | (practical to ) | |
| Du’s sieve | when and have easy prefix sums | ||
| powerful-number trick | when agrees with an easy on primes | plus evaluations | |
| squarefree count | |||
| Meissel–Lehmer / LMO | to + |
Lucy’s prime counting
= numbers in that are prime or have smallest prime factor . Start with and, for each prime in increasing order, remove the numbers whose smallest prime factor is exactly : for every . At the end for all floor values . Weighting by gives prime power sums.
struct Lucy { // pi(v) for every v in {floor(n / i)}
ll n, r; vector<ll> V, S; vector<int> lo, hi;
int id(ll v) const { return v <= r ? lo[v] : hi[n / v]; }
Lucy(ll n) : n(n) {
r = sqrtl(n); while (r * r > n) r--; while ((r + 1) * (r + 1) <= n) r++;
lo.assign(r + 2, 0); hi.assign(r + 2, 0);
for (ll l = 1; l <= n; l = n / (n / l) + 1) { ll v = n / l; (v <= r ? lo[v] : hi[n / v]) = sz(V); V.push_back(v); S.push_back(v - 1); }
for (ll p = 2; p <= r; p++) {
if (S[id(p)] == S[id(p - 1)]) continue; // p is not prime
ll sp = S[id(p - 1)], p2 = p * p;
for (int i = 0; i < sz(V) && V[i] >= p2; i++) S[i] -= S[id(V[i] / p)] - sp; // V is decreasing
}
}
ll pi(ll v) const { return S[id(v)]; }
};Proof: Lucy's recurrence
Let count that are prime or have all prime factors . Going from to removes exactly the composites whose smallest prime factor is : with and having no prime factor , . Those are counted by minus the primes below , i.e. . For nothing changes. After all primes , every composite has been removed (its smallest prime factor is ), so . Updating in decreasing order uses before it is changed in this round (smaller values come later). Cost: .
Min_25 sieve
For multiplicative whose value on primes is a polynomial and with computable: phase 1 runs Lucy for each power to get at all floor values; phase 2 adds composites by smallest prime factor:
, and .
template <class FPE> mint min25(ll n, const vector<mint>& coef, FPE fpe) { // f(p) = sum coef[k] p^k; fpe(p, e) = f(p^e)
ll r = sqrtl(n); while (r * r > n) r--; while ((r + 1) * (r + 1) <= n) r++;
vector<ll> V; vector<int> lo(r + 2), hi(r + 2);
for (ll l = 1; l <= n; l = n / (n / l) + 1) { ll v = n / l; (v <= r ? lo[v] : hi[n / v]) = sz(V); V.push_back(v); }
auto id = [&](ll v) { return v <= r ? lo[v] : hi[n / v]; };
vector<int> primes; { vector<char> c(r + 1); for (ll i = 2; i <= r; i++) if (!c[i]) { primes.push_back(i); for (ll j = i * i; j <= r; j += i) c[j] = 1; } }
int K = sz(coef); vector<mint> G(sz(V), 0);
for (int k = 0; k < K; k++) { // Lucy on p^k: g(v) = sum_{2 <= i <= v} i^k, then sieve
vector<mint> g(sz(V));
for (int i = 0; i < sz(V); i++) {
mint v = V[i];
if (k == 0) g[i] = v - 1; else if (k == 1) g[i] = v * (v + 1) / 2 - 1; else g[i] = v * (v + 1) * (2 * v + 1) / 6 - 1;
}
vector<mint> pp(sz(primes) + 1, 0);
for (int j = 0; j < sz(primes); j++) pp[j + 1] = pp[j] + mint(primes[j]).pow(k);
for (int j = 0; j < sz(primes); j++) {
ll p = primes[j]; mint pk = mint(p).pow(k);
for (int i = 0; i < sz(V) && V[i] >= p * p; i++) g[i] -= pk * (g[id(V[i] / p)] - pp[j]);
}
for (int i = 0; i < sz(V); i++) G[i] += coef[k] * g[i];
}
vector<mint> Gp(sz(primes) + 1, 0); // sum of f(p) over the first j primes
for (int j = 0; j < sz(primes); j++) { mint s = 0, pw = 1; for (int k = 0; k < K; k++) s += coef[k] * pw, pw *= primes[j]; Gp[j + 1] = Gp[j] + s; }
auto S = [&](auto& self, ll x, int j) -> mint { // sum of f(i) over 2 <= i <= x with spf(i) >= primes[j]
if (j < sz(primes) && primes[j] > x) return 0;
mint res = G[id(x)] - Gp[j];
for (int k = j; k < sz(primes) && (ll)primes[k] * primes[k] <= x; k++) {
ll p = primes[k], pe = p;
for (int e = 1; pe * p <= x; e++, pe *= p) res += fpe(p, e) * self(self, x / pe, k + 1) + fpe(p, e + 1);
}
return res;
};
return S(S, n, 0) + 1; // + f(1)
}Supported directly: (: coef = {-1, 1}), ({-1}), ({1, 1}), ({2}), sums of primes ({0, 1} with phase 1 only). Powers up to are built in; add more power-sum formulas for higher degrees.
Proof: Min_25's two phases
Phase 1 is Lucy applied to the completely multiplicative weight : removing composites with smallest prime factor from subtracts because ; at the end , and linearity in the coefficients gives . Phase 2: every with smallest prime factor is either a prime (the term) or with the smallest prime factor, , and either with (the terms, shifted index) or with smallest prime factor (the recursive term, multiplicative because ). Composite need . The recursion visits only floor values, and its cost is in the standard analysis (practically fast).
Du’s sieve (generic)
If with prefix sums , cheap: . Sieve up to , memoise above. The concrete / Mertens version is in Arithmetic Functions.
template <class T> struct DuGeneric { // small[x] = S_f(x) for x <= L; Sg, Sh prefix sums of g, h; g(1) = 1
vector<T> small; function<T(ll)> Sg, Sh; unordered_map<ll, T> memo;
T operator()(ll n) {
if (n < sz(small)) return small[n];
if (auto it = memo.find(n); it != memo.end()) return it->second;
T r = Sh(n);
for (ll l = 2, rr; l <= n; l = rr + 1) { rr = n / (n / l); r -= (Sg(rr) - Sg(l - 1)) * (*this)(n / l); }
return memo[n] = r;
}
};Powerful-number trick
If is multiplicative and is multiplicative with on primes and an easy prefix sum , then vanishes on non-powerful numbers (every prime exponent ), so ; there are only powerful numbers up to , enumerated by DFS over primes . comes from .
template <class FPE, class GPE, class GS> mint powerfulSum(ll n, FPE fpe, GPE gpe, GS G) {
ll r = sqrtl(n); while (r * r > n) r--; while ((r + 1) * (r + 1) <= n) r++;
vector<int> pr; { vector<char> c(r + 1); for (ll i = 2; i <= r; i++) if (!c[i]) { pr.push_back(i); for (ll j = i * i; j <= r; j += i) c[j] = 1; } }
mint res = 0;
auto dfs = [&](auto& self, int k, ll d, mint hd) -> void { // d powerful, h(d) = hd; primes from index k on
res += hd * G(n / d);
for (int j = k; j < sz(pr) && (i128)d * pr[j] * pr[j] <= n; j++) {
ll p = pr[j]; vector<mint> h{1}; // h(p^0..)
ll pe = p * p;
for (int e = 2; (i128)d * pe <= n; e++, pe *= p) {
while (sz(h) <= e) { int t = sz(h); mint v = fpe(p, t); for (int i = 1; i <= t; i++) v -= gpe(p, i) * h[t - i]; h.push_back(v); }
if (h[e] != mint(0)) self(self, j + 1, d * pe, hd * h[e]);
if ((i128)pe * p > n) break;
}
}
};
dfs(dfs, 0, 1, 1);
return res;
}
ll squarefreeCount(ll n) { // sum_{d <= sqrt n} mu(d) floor(n / d^2)
ll r = sqrtl(n); while (r * r > n) r--; while ((r + 1) * (r + 1) <= n) r++;
vector<int> mu(r + 1, 1), mark(r + 1); ll res = 0;
for (ll i = 2; i <= r; i++) if (!mark[i]) for (ll j = i; j <= r; j += i) { mark[j] = 1; mu[j] = (j / i) % i == 0 ? 0 : -mu[j]; }
for (ll d = 1; d <= r; d++) res += mu[d] * (n / (d * d));
return res;
}Proof: powerful numbers, and the squarefree formula
is multiplicative; at a prime , so and is zero on any number with an exponent 1. Then . A powerful number is with squarefree, so there are at most of them. The DFS assigns each powerful its prime factorisation with exponents exactly once. Squarefree: (check on prime powers), and summing over gives ; the inner
musieve marks when and flips the sign per prime factor.