All Euler problems
Project Euler

Mobius Function and Intervals

The Mobius function mu(n) equals (-1)^k if n is squarefree with k distinct prime factors, and 0 otherwise. Define: P(a,b) = |{n in [a,b]: mu(n) = 1}| N(a,b) = |{n in [a,b]: mu(n) = -1}| Let C(n) co...

Source sync Apr 19, 2026
Problem #0464
Level Level 26
Solved By 385
Languages C++, Python
Answer 198775297232878
Length 346 words
linear_algebramodular_arithmeticbrute_force

Problem Statement

This archive keeps the full statement, math, and original media on the page.

The MÃbius function, denoted \(\mu (n)\), is defined as:

  • \(\mu (n) = (-1)^{\omega (n)}\) if \(n\) is squarefree (where \(\omega (n)\) is the number of distinct prime factors of \(n\))

  • \(\mu (n) = 0\) if \(n\) is not squarefree.

Let \(P(a, b)\) be the number of integers \(n\) in the interval \([a, b]\) such that \(\mu (n) = 1\).

Let \(N(a, b)\) be the number of integers \(n\) in the interval \([a, b]\) such that \(\mu (n) = -1\).

For example, \(P(2,10) = 2\) and \(N(2,10) = 4\).

Let \(C(n)\) be the number of integer pairs \((a, b)\) such that:

  • \(1\le a \le b \le n\),

  • \(99 \cdot N(a, b) \le 100 \cdot P(a, b)\), and

  • \(99 \cdot P(a, b) \le 100 \cdot N(a, b)\).

For example, \(C(10) = 13\), \(C(500) = 16676\) and \(C(10\,000) = 20155319\).

Find \(C(20\,000\,000)\).

Problem 464: Mobius Function and Intervals

Mathematical Foundation

Theorem 1 (Reduction to 2D dominance). Define prefix sums prefP(i)={ni:μ(n)=1}\mathrm{prefP}(i) = |\{n \le i : \mu(n) = 1\}| and prefN(i)={ni:μ(n)=1}\mathrm{prefN}(i) = |\{n \le i : \mu(n) = -1\}|, with prefP(0)=prefN(0)=0\mathrm{prefP}(0) = \mathrm{prefN}(0) = 0. Set

X(i)=100prefP(i)99prefN(i),Y(i)=100prefN(i)99prefP(i).X(i) = 100 \cdot \mathrm{prefP}(i) - 99 \cdot \mathrm{prefN}(i), \qquad Y(i) = 100 \cdot \mathrm{prefN}(i) - 99 \cdot \mathrm{prefP}(i).

Then C(n)={(j,b):0j<bn,  X(b)X(j),  Y(b)Y(j)}C(n) = |\{(j, b) : 0 \le j < b \le n,\; X(b) \ge X(j),\; Y(b) \ge Y(j)\}|.

Proof. For a pair (a,b)(a, b), write P(a,b)=prefP(b)prefP(a1)P(a,b) = \mathrm{prefP}(b) - \mathrm{prefP}(a-1) and N(a,b)=prefN(b)prefN(a1)N(a,b) = \mathrm{prefN}(b) - \mathrm{prefN}(a-1). The first inequality 99N(a,b)100P(a,b)99 \cdot N(a,b) \le 100 \cdot P(a,b) becomes

99(prefN(b)prefN(a1))100(prefP(b)prefP(a1)),99(\mathrm{prefN}(b) - \mathrm{prefN}(a-1)) \le 100(\mathrm{prefP}(b) - \mathrm{prefP}(a-1)),

which rearranges to X(b)X(a1)X(b) \ge X(a-1). Similarly, the second inequality yields Y(b)Y(a1)Y(b) \ge Y(a-1). Setting j=a1j = a - 1 (so jj ranges over 0,1,,n10, 1, \ldots, n-1 and bb over j+1,,nj+1, \ldots, n) completes the reduction. \square

Lemma 1 (Monotonicity observation). X(i)+Y(i)=prefP(i)+prefN(i)X(i) + Y(i) = \mathrm{prefP}(i) + \mathrm{prefN}(i), which counts squarefree numbers up to ii and is non-decreasing.

Proof. Direct computation: X(i)+Y(i)=100prefP(i)99prefN(i)+100prefN(i)99prefP(i)=prefP(i)+prefN(i)X(i) + Y(i) = 100\,\mathrm{prefP}(i) - 99\,\mathrm{prefN}(i) + 100\,\mathrm{prefN}(i) - 99\,\mathrm{prefP}(i) = \mathrm{prefP}(i) + \mathrm{prefN}(i). This is the count of squarefree integers i\le i, which is non-decreasing. \square

Theorem 2 (CDQ divide-and-conquer). The 2D dominance count with the ordering constraint j<bj < b can be computed in O(nlog2n)O(n \log^2 n) time.

Proof. We use the CDQ (Chen, Deng, Qin) offline divide-and-conquer framework. Divide the index range [0,n][0, n] at midpoint mm. Recursively count pairs where both indices lie in [0,m][0, m] or both in (m,n](m, n]. For cross-pairs (jm<bj \le m < b):

  1. Sort the left half L={0,,m}L = \{0, \ldots, m\} and right half R={m+1,,n}R = \{m+1, \ldots, n\} independently by XX-value.
  2. Two-pointer sweep: process elements of RR in increasing XX order. For each bRb \in R, insert all jLj \in L with X(j)X(b)X(j) \le X(b) into a Fenwick tree keyed by coordinate-compressed Y(j)Y(j).
  3. Query the Fenwick tree for the prefix sum up to Y(b)Y(b), counting all jj with Y(j)Y(b)Y(j) \le Y(b).

The merge step takes O(klogk)O(k \log k) for a subproblem of size kk. With O(logn)O(\log n) levels, the total is O(nlog2n)O(n \log^2 n). Correctness follows because every valid pair (j,b)(j, b) with j<bj < b is counted exactly once: either both lie in the same half (handled recursively) or they straddle the midpoint (handled by the merge step). \square

Editorial

Count pairs (a,b) with 1 <= a <= b <= n such that the counts of mu=+1 and mu=-1 values in [a,b] are within a 100:99 ratio. Reformulation: 2D dominance counting via CDQ divide-and-conquer. X(i) = 100prefP(i) - 99prefN(i), Y(i) = 100prefN(i) - 99prefP(i). Count pairs (j, b) with j < b, X(b) >= X(j), Y(b) >= Y(j). We sieve Mobius function. We then compute prefix sums and coordinates. Finally, we apply a CDQ divide-and-conquer sweep.

Pseudocode

Sieve Mobius function
Compute prefix sums and coordinates
CDQ divide-and-conquer
Cross-pairs: j in [lo, mid], b in [mid+1, hi]
Coordinate-compress Y values of L
Two-pointer + Fenwick tree on Y

Complexity Analysis

  • Time: O(n)O(n) for the Mobius sieve. O(nlog2n)O(n \log^2 n) for the CDQ divide-and-conquer (O(logn)O(\log n) levels, each doing O(nlogn)O(n \log n) work for sorting and Fenwick tree operations).
  • Space: O(n)O(n) for the sieve, prefix sums, and auxiliary arrays.

Answer

198775297232878\boxed{198775297232878}

Code

Each problem page includes the exact C++ and Python source files from the local archive.

C++ project_euler/problem_464/solution.cpp
#include <bits/stdc++.h>
using namespace std;

/*
 * Problem 464: Mobius Function and Intervals
 *
 * Count pairs (a,b) with 1 <= a <= b <= n where the counts of
 * mu=+1 and mu=-1 in [a,b] are within ratio 100:99.
 *
 * Reformulation: 2D dominance counting.
 * X(i) = 100*prefP(i) - 99*prefN(i)
 * Y(i) = 100*prefN(i) - 99*prefP(i)
 * Count pairs (j,b), j < b, X(b)>=X(j), Y(b)>=Y(j).
 *
 * Algorithm: CDQ divide-and-conquer with Fenwick tree.
 * Time: O(N log^2 N). Space: O(N).
 */

const int MAXN = 20000001;

int mu_arr[MAXN];
bool is_prime[MAXN];
int primes[1500000];
int pcnt = 0;

int X_arr[MAXN], Y_arr[MAXN];
int idx_arr[MAXN], tmp[MAXN];
int Y_comp[MAXN];
long long answer = 0;

// Fenwick tree
int bit[MAXN];
int bit_size;

void bit_update(int i, int v) {
    for (i++; i <= bit_size; i += i & (-i))
        bit[i] += v;
}

int bit_query(int i) {
    int s = 0;
    for (i++; i > 0; i -= i & (-i))
        s += bit[i];
    return s;
}

void bit_clear(int i) {
    for (i++; i <= bit_size; i += i & (-i))
        bit[i] = 0;
}

void mobius_sieve(int n) {
    memset(is_prime, true, sizeof(is_prime));
    mu_arr[1] = 1;
    for (int i = 2; i <= n; i++) {
        if (is_prime[i]) {
            primes[pcnt++] = i;
            mu_arr[i] = -1;
        }
        for (int j = 0; j < pcnt && (long long)i * primes[j] <= n; j++) {
            is_prime[i * primes[j]] = false;
            if (i % primes[j] == 0) {
                mu_arr[i * primes[j]] = 0;
                break;
            }
            mu_arr[i * primes[j]] = -mu_arr[i];
        }
    }
}

// CDQ divide and conquer
// arr[lo..hi) are indices, to be merge-sorted by X
void cdq(int lo, int hi) {
    if (hi - lo <= 1) return;
    int mid = (lo + hi) / 2;
    cdq(lo, mid);
    cdq(mid, hi);

    // Count cross: j in [lo,mid), b in [mid,hi), X[b]>=X[j], Y[b]>=Y[j]
    // Both halves are sorted by X after recursion
    int li = lo;
    vector<int> inserted;
    for (int ri = mid; ri < hi; ri++) {
        int b = idx_arr[ri];
        while (li < mid && X_arr[idx_arr[li]] <= X_arr[b]) {
            int j = idx_arr[li];
            bit_update(Y_comp[j], 1);
            inserted.push_back(Y_comp[j]);
            li++;
        }
        answer += bit_query(Y_comp[b]);
    }
    for (int yc : inserted) bit_clear(yc);

    // Merge sort by X
    int i = lo, j = mid, k = lo;
    while (i < mid && j < hi) {
        if (X_arr[idx_arr[i]] <= X_arr[idx_arr[j]])
            tmp[k++] = idx_arr[i++];
        else
            tmp[k++] = idx_arr[j++];
    }
    while (i < mid) tmp[k++] = idx_arr[i++];
    while (j < hi) tmp[k++] = idx_arr[j++];
    for (int t = lo; t < hi; t++) idx_arr[t] = tmp[t];
}

int main() {
    int N = 20000000;
    mobius_sieve(N);

    // Prefix sums
    int prefP = 0, prefN = 0;
    X_arr[0] = 0;
    Y_arr[0] = 0;
    for (int i = 1; i <= N; i++) {
        if (mu_arr[i] == 1) prefP++;
        else if (mu_arr[i] == -1) prefN++;
        X_arr[i] = 100 * prefP - 99 * prefN;
        Y_arr[i] = 100 * prefN - 99 * prefP;
    }

    // Coordinate compress Y
    vector<int> Y_vals(Y_arr, Y_arr + N + 1);
    sort(Y_vals.begin(), Y_vals.end());
    Y_vals.erase(unique(Y_vals.begin(), Y_vals.end()), Y_vals.end());
    bit_size = Y_vals.size();
    for (int i = 0; i <= N; i++) {
        Y_comp[i] = lower_bound(Y_vals.begin(), Y_vals.end(), Y_arr[i]) - Y_vals.begin();
    }

    // CDQ
    for (int i = 0; i <= N; i++) idx_arr[i] = i;
    memset(bit, 0, sizeof(bit));
    cdq(0, N + 1);

    cout << answer << endl;
    return 0;
}