All Euler problems
Project Euler

3-Like Numbers

For a positive integer n, define f(n) as the number of non-empty substrings of n (in decimal) that are divisible by 3. Call n 3-like if f(n) equiv 0 (mod 3). Let F(d) count the d -digit 3-like numb...

Source sync Apr 19, 2026
Problem #0706
Level Level 19
Solved By 700
Languages C++, Python
Answer 884837055
Length 458 words
modular_arithmeticlinear_algebradynamic_programming

Problem Statement

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

For a positive integer \(n\), define \(f(n)\) to be the number of non-empty substrings of \(n\) that are divisible by \(3\). For example, the string "2573" has \(10\) non-empty substrings, three of which represent numbers that are divisible by \(3\), namely \(57\), \(573\) and \(3\). So \(f(2573) = 3\).

If \(f(n)\) is divisible by \(3\) then we say that \(n\) is \(3\)-like.

Define \(F(d)\) to be how many \(d\) digit numbers are \(3\)-like. For example, \(F(2) = 30\) and \(F(6) = 290898\).

Find \(F(10^5)\). Give your answer modulo \(1\,000\,000\,007\).

Problem 706: 3-Like Numbers

Mathematical Foundation

Lemma 1 (Substring Divisibility via Prefix Sums). A substring from position ii to position jj (1-indexed) of a number with digits d1d2dkd_1 d_2 \cdots d_k is divisible by 3 if and only if SjSi1(mod3)S_j \equiv S_{i-1} \pmod{3}, where Sr==1rdS_r = \sum_{\ell=1}^{r} d_\ell is the prefix digit sum and S0=0S_0 = 0.

Proof. A number is divisible by 3 iff its digit sum is divisible by 3. The digit sum of the substring from ii to jj is SjSi1S_j - S_{i-1}, which is divisible by 3 iff SjSi1(mod3)S_j \equiv S_{i-1} \pmod{3}. \square

Theorem 1 (Counting Formula). Let cr={k{0,1,,d}:Skr(mod3)}c_r = |\{k \in \{0, 1, \ldots, d\} : S_k \equiv r \pmod{3}\}| for r=0,1,2r = 0, 1, 2, where c0+c1+c2=d+1c_0 + c_1 + c_2 = d + 1. Then

f(n)=(c02)+(c12)+(c22).f(n) = \binom{c_0}{2} + \binom{c_1}{2} + \binom{c_2}{2}.

Proof. Each pair (i1,j)(i-1, j) with 0i1<jd0 \le i-1 < j \le d and SjSi1(mod3)S_j \equiv S_{i-1} \pmod{3} corresponds to a substring divisible by 3. The number of such pairs with Si1SjrS_{i-1} \equiv S_j \equiv r is (cr2)\binom{c_r}{2}. Summing over r=0,1,2r = 0, 1, 2 gives the total. \square

Lemma 2 (Reduction Modulo 3). For cZ0c \in \mathbb{Z}_{\ge 0}:

(c2)mod3={0if c0(mod3),0if c1(mod3),1if c2(mod3).\binom{c}{2} \bmod 3 = \begin{cases} 0 & \text{if } c \equiv 0 \pmod{3}, \\ 0 & \text{if } c \equiv 1 \pmod{3}, \\ 1 & \text{if } c \equiv 2 \pmod{3}. \end{cases}

Proof. Direct computation: (c2)=c(c1)/2\binom{c}{2} = c(c-1)/2. For c0c \equiv 0: c0c \equiv 0, so 3c(c1)/23 \mid c(c-1)/2. For c1c \equiv 1: c10c-1 \equiv 0, so 3c(c1)/23 \mid c(c-1)/2. For c2c \equiv 2: c(c1)/221/21(mod3)c(c-1)/2 \equiv 2 \cdot 1/2 \equiv 1 \pmod{3}. \square

Theorem 2 (State-Based DP with Matrix Exponentiation). The 3-like condition f(n)0(mod3)f(n) \equiv 0 \pmod{3} depends only on (c0mod3,c1mod3,c2mod3)(c_0 \bmod 3, c_1 \bmod 3, c_2 \bmod 3). This gives a state space of 33=273^3 = 27 states. The transitions when appending a digit depend on the current prefix sum modulo 3 (which determines which crc_r to increment) and the digit’s residue modulo 3.

Proof. The current prefix sum modulo 3 is determined by which counter was last incremented. Appending a digit dd changes the prefix sum from ss to (s+d)mod3(s + d) \bmod 3, incrementing c(s+d)mod3c_{(s+d) \bmod 3} by 1. Since we track counters modulo 3, the new state is fully determined. Among digits 0—9, there are 4 with residue 0 (namely 0, 3, 6, 9), 3 with residue 1 (1, 4, 7), and 3 with residue 2 (2, 5, 8). The first digit excludes 0, but digit 0 has residue 0, so the first-digit distribution is (3, 3, 3) and subsequent digits use (4, 3, 3). This yields a 27×2727 \times 27 transition matrix MM that can be exponentiated. \square

Theorem 3 (Final Count). F(d)F(d) is obtained by:

  1. Setting the initial state vector v0\mathbf{v}_0 from the first digit (which is nonzero).
  2. Computing vd=Md1v0\mathbf{v}_d = M^{d-1} \mathbf{v}_0.
  3. Summing entries of vd\mathbf{v}_d corresponding to accepting states (those with (c02)+(c12)+(c22)0(mod3)\binom{c_0}{2} + \binom{c_1}{2} + \binom{c_2}{2} \equiv 0 \pmod{3}).

Proof. The matrix power Md1M^{d-1} propagates the state distribution through d1d - 1 digit appends (after the first digit). The accepting condition is evaluated on the final state using Lemma 2. \square

Editorial

For a positive integer n, f(n) = number of non-empty substrings divisible by 3. n is 3-like if f(n) is divisible by 3. F(d) = count of d-digit 3-like numbers. Find F(10^5) mod 1_000_000_007. We state: (c0 mod 3, c1 mod 3, c2 mod 3) — 27 states. We then initial: S_0 = 0, so c0 = 1, c1 = 0, c2 = 0. Finally, build 27x27 transition matrix M for one digit (digits 0-9).

Pseudocode

State: (c0 mod 3, c1 mod 3, c2 mod 3) — 27 states
Initial: S_0 = 0, so c0 = 1, c1 = 0, c2 = 0
prefix_sum = 0
Build 27x27 transition matrix M for one digit (digits 0-9)
Digit residues: {0,3,6,9}→0, {1,4,7}→1, {2,5,8}→2
Counts: (4, 3, 3)
Increment c_{new_s} mod 3
Handle first digit: only digits 1-9 (residues: 3 each)
Initialize state after first digit + S_0
After S_0=0: c0=1. First digit sets S_1 = r, so c_r += 1
Apply M^{d-1} to v
Sum accepting states

Complexity Analysis

  • Time: O(273logd)=O(logd)O(27^3 \cdot \log d) = O(\log d) since the matrix size is a constant (27×2727 \times 27) and we use fast matrix exponentiation with O(logd)O(\log d) squarings.
  • Space: O(272)=O(1)O(27^2) = O(1) for the transition matrix.

Answer

884837055\boxed{884837055}

Code

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

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

const long long MOD = 1000000007;

// State: (c0 mod 3, c1 mod 3, c2 mod 3) -> 27 states
// But we also need to know current prefix sum mod 3 to determine which counter to increment.
// current prefix sum mod 3 is determined by which c_r we last incremented.
// Actually, we need to track prefix_sum mod 3 explicitly as part of the state.
// State: (c0 mod 3, c1 mod 3, c2 mod 3, prefix_sum mod 3) but prefix_sum mod 3
// is not independent - no, it tells us which counter gets incremented next.
// Actually prefix_sum is the current S_k mod 3. When we add digit d, new S = (S + d) mod 3,
// and we increment c_{new_S}. So we need to track current S mod 3.
// Total states: 27 * 3 = 81

typedef vector<vector<long long>> Matrix;

int stateIndex(int c0, int c1, int c2, int s) {
    return ((c0 * 3 + c1) * 3 + c2) * 3 + s;
}

Matrix multiply(const Matrix& A, const Matrix& B, int n) {
    Matrix C(n, vector<long long>(n, 0));
    for (int i = 0; i < n; i++)
        for (int k = 0; k < n; k++) {
            if (A[i][k] == 0) continue;
            for (int j = 0; j < n; j++)
                C[i][j] = (C[i][j] + A[i][k] * B[k][j]) % MOD;
        }
    return C;
}

Matrix matpow(Matrix M, long long p, int n) {
    Matrix result(n, vector<long long>(n, 0));
    for (int i = 0; i < n; i++) result[i][i] = 1;
    while (p > 0) {
        if (p & 1) result = multiply(result, M, n);
        M = multiply(M, M, n);
        p >>= 1;
    }
    return result;
}

int main() {
    const int N = 81; // 27 * 3
    const long long D = 100000; // 10^5 digits

    // Build transition matrix
    Matrix T(N, vector<long long>(N, 0));

    // digits mod 3: 0,3,6,9 -> residue 0 (4 digits)
    //               1,4,7   -> residue 1 (3 digits)
    //               2,5,8   -> residue 2 (3 digits)
    int digitCount[3] = {4, 3, 3};

    for (int c0 = 0; c0 < 3; c0++)
        for (int c1 = 0; c1 < 3; c1++)
            for (int c2 = 0; c2 < 3; c2++)
                for (int s = 0; s < 3; s++) {
                    int from = stateIndex(c0, c1, c2, s);
                    // Add a digit with residue r (0,1,2)
                    for (int r = 0; r < 3; r++) {
                        int newS = (s + r) % 3;
                        int nc0 = c0, nc1 = c1, nc2 = c2;
                        if (newS == 0) nc0 = (nc0 + 1) % 3;
                        else if (newS == 1) nc1 = (nc1 + 1) % 3;
                        else nc2 = (nc2 + 1) % 3;
                        int to = stateIndex(nc0, nc1, nc2, newS);
                        T[to][from] = (T[to][from] + digitCount[r]) % MOD;
                    }
                }

    // Initial state: c0=1 (for S_0=0), c1=0, c2=0, prefix_sum=0
    // But first digit can't be 0 (d-digit number), so we handle first digit separately
    // First digit: 1-9
    // digit residues for 1-9: residue 0: {3,6,9} -> 3 digits
    //                          residue 1: {1,4,7} -> 3 digits
    //                          residue 2: {2,5,8} -> 3 digits

    vector<long long> state(N, 0);
    // Initial: c0=1 (S_0=0 counted), c1=0, c2=0, current prefix_sum=0
    // After first digit d with residue r:
    // new prefix_sum = r, increment c_r
    for (int r = 0; r < 3; r++) {
        int c0 = 1, c1 = 0, c2 = 0;
        int newS = r;
        if (newS == 0) c0 = (c0 + 1) % 3; // c0 becomes 2
        else if (newS == 1) c1 = 1;
        else c2 = 1;
        int idx = stateIndex(c0 % 3, c1, c2, newS);
        int cnt = 3; // 3 digits for each residue among 1-9
        state[idx] = (state[idx] + cnt) % MOD;
    }

    // Apply transition matrix D-1 times for remaining digits
    if (D > 1) {
        Matrix Tp = matpow(T, D - 1, N);
        vector<long long> newState(N, 0);
        for (int i = 0; i < N; i++)
            for (int j = 0; j < N; j++)
                newState[i] = (newState[i] + Tp[i][j] * state[j]) % MOD;
        state = newState;
    }

    // Sum states where binom(c0,2) + binom(c1,2) + binom(c2,2) = 0 mod 3
    // binom_mod3[c mod 3]: c=0->0, c=1->0, c=2->1
    int binom_mod3[3] = {0, 0, 1};

    long long answer = 0;
    for (int c0 = 0; c0 < 3; c0++)
        for (int c1 = 0; c1 < 3; c1++)
            for (int c2 = 0; c2 < 3; c2++) {
                int val = (binom_mod3[c0] + binom_mod3[c1] + binom_mod3[c2]) % 3;
                if (val == 0) {
                    for (int s = 0; s < 3; s++) {
                        int idx = stateIndex(c0, c1, c2, s);
                        answer = (answer + state[idx]) % MOD;
                    }
                }
            }

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