All Euler problems
Project Euler

Simbers

A positive integer is called a simber if every decimal digit that appears in its representation appears an even number of times. For example, 1221 is a simber (two 1s and two 2s), but 1231 is not (...

Source sync Apr 19, 2026
Problem #0520
Level Level 23
Solved By 499
Languages C++, Python
Answer 238413705
Length 491 words
dynamic_programmingdigit_dplinear_algebra

Problem Statement

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

We define a simber to be a positive integer in which any odd digit, if present, occurs an odd number of times, and any even digit, if present, occurs an even number of times.

For example, \(141221242\) is a \(9\)-digit simber because it has three \(1\)’s, four \(2\)’s and two \(4\)’s.

Let \(Q(n)\) be the count of all simbers with at most \(n\) digits.

You are given \(Q(7) = 287975\) and \(Q(100) \bmod 1\,000\,000\,123 = 123864868\).

Find \((\sum _{1 \le u \le 39} Q(2^u)) \bmod 1\,000\,000\,123\).

Problem 520: Simbers

Mathematical Foundation

Theorem 1 (Parity Bitmask Characterization). A positive integer mm is a simber if and only if, letting d1d2dLd_1 d_2 \cdots d_L be its decimal representation, the XOR-parity vector

mask=i=1Ledi{0,1}10\text{mask} = \bigoplus_{i=1}^{L} e_{d_i} \in \{0,1\}^{10}

equals the zero vector, where eje_j is the standard basis vector with a 1 in position jj and \oplus denotes componentwise XOR (equivalently, addition modulo 2).

Proof. The jj-th component of the mask is i=1L[di=j]mod2\sum_{i=1}^{L}[d_i = j] \bmod 2, which is 0 if and only if digit jj appears an even number of times. Thus mask=0\text{mask} = \mathbf{0} if and only if every digit appears an even number of times. \square

Theorem 2 (Digit DP Correctness). The DP with states (pos,mask,tight,started)(\text{pos}, \text{mask}, \text{tight}, \text{started}) correctly counts all integers in [1,N][1, N] that are simbers.

Proof. We construct each integer mNm \leq N digit-by-digit from the most significant position.

  • Position: pos{0,1,,L1}\text{pos} \in \{0, 1, \ldots, L-1\} where LL is the number of digits of NN.
  • Mask: mask{0,1}10\text{mask} \in \{0,1\}^{10} tracks the parity of each digit’s occurrence count so far.
  • Tight: A boolean indicating whether the prefix constructed so far matches NN exactly (constraining future digits).
  • Started: A boolean indicating whether a nonzero digit has been placed (to handle leading zeros correctly: leading zeros should not flip the mask for digit 0).

Transitions: At position pos\text{pos}, choose digit d{0,,}d \in \{0, \ldots, \ell\} where =dpos\ell = d_{\text{pos}} if tight, else =9\ell = 9.

  • If not started and d=0d = 0: proceed with mask unchanged, still not started.
  • Otherwise: set started=true\text{started} = \text{true}, maskmask(1d)\text{mask} \leftarrow \text{mask} \oplus (1 \ll d).

Base case: At pos=L\text{pos} = L, the number is a simber iff started=true\text{started} = \text{true} and mask=0\text{mask} = 0.

Each integer m[0,N]m \in [0, N] corresponds to exactly one path through the DP. The started flag ensures m=0m = 0 is not counted (or counted separately if needed). \square

Lemma 1 (State Space Bound). The number of distinct DP states is at most L×210×2×2=4096LL \times 2^{10} \times 2 \times 2 = 4096L.

Proof. There are LL positions, 210=10242^{10} = 1024 possible masks, 2 values for tight, and 2 values for started. \square

Lemma 2 (Simplification for N=10nN = 10^n). When N=10nN = 10^n, we can separately count simbers with exactly \ell digits for =1,2,,n\ell = 1, 2, \ldots, n. For exactly \ell digits, the leading digit d1{1,,9}d_1 \in \{1, \ldots, 9\} and the remaining 1\ell - 1 digits are in {0,,9}\{0, \ldots, 9\}, with the constraint that the final mask is 0\mathbf{0}.

Proof. Since 10n10^n itself is not a simber (digit 1 appears once), Q(n)Q(n) counts simbers in [1,10n1][1, 10^n - 1], which is the union of simbers with \ell digits for =1,,n\ell = 1, \ldots, n. For a fixed digit length, there is no upper-bound constraint (all \ell-digit numbers are <10n< 10^n), so the tight constraint is trivially released after the first digit, and we can use the combinatorial formula directly. \square

Editorial

A simber is a number where every digit appears an even number of times. Count simbers up to 10^n using digit DP with parity bitmask. We count simbers in [1, 10^n - 1]. We then count L-digit numbers with all-even digit parities. Finally, such that the parity mask becomes 0.

Pseudocode

Count simbers in [1, 10^n - 1]
For each digit length L = 1, ..., n:
Count L-digit numbers with all-even digit parities
dp[mask] = number of ways to fill remaining positions
such that the parity mask becomes 0
Process position by position
Count L-digit simbers
Position 0: digit d1 in {1, ..., 9}
Positions 1..L-1: digit in {0, ..., 9}
DP over positions, tracking mask
After choosing d1:

Complexity Analysis

  • Time: For each digit length LL (from 1 to nn), the DP processes LL positions with 210=10242^{10} = 1024 mask states and 10 digit choices per state. Total: O(nn102410)=O(10240n2)O(n \cdot n \cdot 1024 \cdot 10) = O(10240\, n^2). This can be optimized to O(n102410)O(n \cdot 1024 \cdot 10) by processing all digit lengths simultaneously.
  • Space: O(210)=O(1024)O(2^{10}) = O(1024) for the DP table (using rolling arrays).

Answer

238413705\boxed{238413705}

Code

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

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

// Digit DP for counting simbers up to N
// Parity bitmask: bit i = 1 means digit i appeared odd times

string num;
int L;
map<tuple<int,int,int,int>, long long> memo;

long long dp(int pos, int mask, int tight, int started) {
    if (pos == L) {
        return (started && mask == 0) ? 1 : 0;
    }
    auto key = make_tuple(pos, mask, tight, started);
    auto it = memo.find(key);
    if (it != memo.end()) return it->second;

    int limit = tight ? (num[pos] - '0') : 9;
    long long total = 0;
    for (int d = 0; d <= limit; d++) {
        int nt = tight && (d == limit);
        if (!started && d == 0) {
            total += dp(pos + 1, mask, nt, 0);
        } else {
            total += dp(pos + 1, mask ^ (1 << d), nt, 1);
        }
    }
    return memo[key] = total;
}

long long count_simbers(long long N) {
    num = to_string(N);
    L = num.size();
    memo.clear();
    return dp(0, 0, 1, 0);
}

int main() {
    // Q(10^n) for n = 1..15
    for (int n = 1; n <= 15; n++) {
        long long N = 1;
        for (int i = 0; i < n; i++) N *= 10;
        long long q = count_simbers(N);
        cout << "Q(10^" << n << ") = " << q << endl;
    }
    return 0;
}