“Sieving” means: for each candidate, touch all of its multiples. The total cost is the harmonic series, — or when restricted to primes.

The complexity facts

LoopCost
for each : for each multiple of
for each prime : for each multiple
for each prime : starting at still , smaller constant
linear sieve (each composite marked once)

Prime sieve

vector<bool> comp(N + 1, false);
for (int i = 2; (long long)i * i <= N; i++)
    if (!comp[i])
        for (int j = i * i; j <= N; j += i) comp[j] = true;

Memory: vector<bool> is bit-packed, so costs 12 MB. Odd-only sieving halves it again.

Linear sieve — smallest prime factor

vector<int> spf(N + 1, 0), primes;
for (int i = 2; i <= N; i++) {
    if (!spf[i]) { spf[i] = i; primes.push_back(i); }
    for (int p : primes) {
        if (p > spf[i] || (long long)i * p > N) break;
        spf[i * p] = p;
    }
}

The p > spf[i] break is what guarantees each composite is marked exactly once — by its smallest prime factor. This gives and an factorisation for every .

Sieving multiplicative functions

Any multiplicative can be computed for all in alongside the linear sieve, by handling three cases:

if (!spf[i])            f[i] = fOfPrime(i);                    // i is prime
else if (i % p == 0)    f[i * p] = fOfPrimePower(...);         // p divides i: same prime
else                    f[i * p] = f[i] * f[p];                // coprime: multiplicative

Works for , , , , and any function you can evaluate on prime powers.

Sieving over multiples — the general pattern

// for every i, accumulate something over its multiples
for (int i = 1; i <= N; i++)
    for (int j = i; j <= N; j += i)
        f[j] += g[i];

, and it computes any Dirichlet-style transform. Uses:

TaskBody
Number of divisorsd[j]++
Sum of divisorssig[j] += i
Möbius transform (subset-sum over divisors)f[j] += g[i]
Count multiples of each in an arraycnt[i] += present[j]
-based countingcount how many array elements each divides

The gcd-counting idiom

// cntDiv[d] = how many array elements are divisible by d
for (int d = 1; d <= N; d++)
    for (int m = d; m <= N; m += d) cntDiv[d] += freq[m];
// then exactly[d] = pairs with gcd exactly d, by descending Möbius
for (int d = N; d >= 1; d--) {
    exact[d] = C(cntDiv[d], 2);
    for (int m = 2*d; m <= N; m += d) exact[d] -= exact[m];
}

This solves “count pairs/subsets with a given gcd” in — one of the most reused number-theory patterns in competitive programming.

Segmented sieve

Primes in with and :

vector<bool> comp(R - L + 1, false);
for (long long p : primesUpToSqrtR) {
    long long start = max(p * p, (L + p - 1) / p * p);
    for (long long j = start; j <= R; j += p) comp[j - L] = true;
}
if (L == 1) comp[0] = true;                     // 1 is not prime

Memory instead of .

Block sieving for cache

For , process the array in blocks of ~32 KB so each block fits in L1 cache. Typically a 2-3× speedup over a flat sieve, because the flat version thrashes memory.

Sieving in other domains

The idea generalises well beyond primes:

  • Sieve of divisors — as above.
  • Sieve over subsetsSOS DP is a sieve over the subset lattice, .
  • Sieve over a range of exponents — for problems about .
  • Sieving a DP — “for each state, push to all reachable states” is the same harmonic pattern when transitions are multiples.

The rule of thumb

Whenever you catch yourself writing “for each , loop over its divisors”, flip it to “for each , loop over its multiples”. Same answer, instead of .

See also: Sieve of Eratosthenes · Divisor Functions · Möbius Function