Multiplicative Functions in Competitive Programming
Common form: asked to sum or count something over , or over pairs , the quantity involves divisors, gcds, or Euler’s totient, and the obvious double loop is hopelessly slow. A surprising fraction of them collapse the moment we notice that the function in play is multiplicative.
Arithmetic and multiplicative functions
An arithmetic function is just a map — in practice, some integer we compute from . It is multiplicative if and
and completely multiplicative if that identity holds for all , coprime or not. The coprimality restriction is the entire personality of the definition: it ties to the prime factorization and to nothing else.
Lemma. A multiplicative function is completely determined by its values on prime powers. If , then .
Proof. The factors are pairwise coprime, so apply the multiplicative rule times.
This is the whole reason these functions are cheap to compute: we never need to reason about a general , only about prime powers , and then multiply. Every algorithm below is, at heart, “do something clever at prime powers, then multiply out.”
Sources:
https://usaco.guide/adv/prefix-sums-nt-1?lang=cpp
https://usaco.guide/adv/prefix-sums-nt-2?lang=cpp
https://codeforces.com/blog/entry/53925
https://codeforces.com/blog/entry/54150
Möbius inversion
Because , convolving any identity by undoes a convolution by :
Möbius inversion. For arithmetic functions ,
In convolution language this is just , and the proof is one line: convolve both sides by and use . This equivalence is the engine behind essentially every “sum over coprime pairs” trick below.
Dirichlet convolution
The reason these functions form a tidy algebra is the Dirichlet convolution:
Its identity element is , since in only the divisor survives. The single fact that makes convolution useful in a contest is this:
Theorem. If and are multiplicative, so is .
Proof. Take . Every divisor of factors uniquely as with , , and . Then
using multiplicativity of and across the coprime split . The right-hand side is .
So multiplicative functions are closed under , and almost all the standard ones are secretly convolutions of simpler functions. The four identities to keep in memory:
Read aloud: “the number of divisors is ”; “the sum of divisors is ”; ""; and the headline act, "". That last one says is the Dirichlet inverse of .
Use case 1 — the linear sieve
Because a multiplicative is pinned down by its prime-power values, we can fill an array in with a linear sieve. The sieve visits every composite exactly once, through its smallest prime factor , so for each new number we only need a rule for two cases:
- . Then , so — pure multiplicativity.
- . Now is also the smallest prime of , and we are bumping its exponent: becomes . We need a rule relating to , usually by tracking the relevant prime-power factor on the side.
Here is the canonical version that produces and together, since they exercise both styles of recurrence:
const int N = 1000001;
int primes[N], pc = 0;
bool composite[N];
int phi[N], mu[N];
void sieve(int n) {
phi[1] = 1; mu[1] = 1;
for (int i = 2; i <= n; i++) {
if (!composite[i]) { // i is prime
primes[pc++] = i;
phi[i] = i - 1; // phi(p) = p - 1
mu[i] = -1; // mu(p) = -1
}
for (int j = 0; j < pc && (long long) i * primes[j] <= n; j++) {
int p = primes[j], ip = i * p;
composite[ip] = true;
if (i % p == 0) { // case 2: p divides i
phi[ip] = phi[i] * p; // phi(p^{k+1}) = p * phi(p^k)
mu[ip] = 0; // squared prime factor => mu = 0
break; // preserve the spf invariant
} else { // case 1: coprime
phi[ip] = phi[i] * (p - 1);
mu[ip] = -mu[i];
}
}
}
}
Why is it linear? The break stops at the smallest prime factor, guaranteeing each composite is struck exactly once. Drop it and this falls back to the sieve.
The same pattern computes anything multiplicative; we only swap the two recurrences. For the number of divisors we track the exponent of the smallest prime, since :
int d[N], cnt[N]; // cnt[i] = exponent of the smallest prime factor of i
void sieve_d(int n) {
d[1] = 1;
for (int i = 2; i <= n; i++) {
if (!composite[i]) { primes[pc++] = i; d[i] = 2; cnt[i] = 1; }
for (int j = 0; j < pc && (long long) i * primes[j] <= n; j++) {
int p = primes[j], ip = i * p;
composite[ip] = true;
if (i % p == 0) {
cnt[ip] = cnt[i] + 1;
d[ip] = d[i] / (cnt[i] + 1) * (cnt[i] + 2); // (e+1) -> (e+2)
break;
} else {
cnt[ip] = 1;
d[ip] = d[i] * 2; // multiply by d(p) = 2
}
}
}
}
Divisor sum works identically if we instead keep the running value for the smallest prime power. The pattern never changes: a value at primes, a step rule at prime powers, multiplicativity everywhere else.
Use case 2 — Möbius inversion and gcd sums
Here is the payoff that makes the algebra worth it. A classic task:
Problem. Given (say up to ), compute .
The naive double loop is — out of reach. But is built from through the identity , i.e. . Apply it to :
Now push the sum over to the outside. The number of divisible by is , and the and choices are independent, so
Sieve in , then evaluate the sum in another . Done.
// G(n) = sum_{d=1..n} phi(d) * floor(n/d)^2, assuming phi[] is already sieved.
long long gcd_sum(int n) {
long long total = 0;
for (int d = 1; d <= n; d++) {
long long q = n / d;
total += (long long) phi[d] * q * q;
}
return total;
}
The same move with in place of counts coprime pairs: replace the condition by to get
which is just inclusion–exclusion over “both divisible by ,” with supplying the signs. Once we can spot this shape by pushing the divisor sum outward, then replace a count by a floor.
Use case 3 — sublinear prefix sums (the Du sieve)
Source: https://oi-wiki.org/math/number-theory/du/ (I still don’t really understand this one)
Question: What if is and an array is impossible?
We often need only a prefix sum of a multiplicative function, such as or . The trick (the “Du sieve”) is to convolve with and read the identity sideways.
Summing over and grouping the double sum by the quotient gives
so, peeling off the term ,
The floor takes only distinct values, so with divisor blocking plus memoization and a linear-sieve precompute up to about , hence, the whole recursion costs . The Möbius version is even cleaner, because collapses the left side to :
I will leave the implementation out since I don’t have a good one, neither I have seen it in a real problem. Top level idea involves a memoization map plus the standard block loop. The takeaway here is the pattern: convolve the target with something whose prefix sum is trivial, then recurse on the quotients.
Takeaways
What I like about multiplicative functions is how mechanical they make a subject that looks intimidating. It comes down to three reflexes:
- Recognize that the function depends only on the prime factorization, so it is pinned down at prime powers.
- Sieve it in when we need every value, using a prime rule and a prime-power step rule.
- Invert with (or convolve with , , ) to turn a gcd or coprimality condition into a clean sum over divisors — then push that sum outward and replace counts by floors.
Almost every “sum over of some gcd thing” problem is one of these reflexes in disguise. Once we start seeing the divisor sum waiting to be pushed outside, we see it everywhere.