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 (...
Problem Statement
This archive keeps the full statement, math, and original media on the page.
We define a
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 is a simber if and only if, letting be its decimal representation, the XOR-parity vector
equals the zero vector, where is the standard basis vector with a 1 in position and denotes componentwise XOR (equivalently, addition modulo 2).
Proof. The -th component of the mask is , which is 0 if and only if digit appears an even number of times. Thus if and only if every digit appears an even number of times.
Theorem 2 (Digit DP Correctness). The DP with states correctly counts all integers in that are simbers.
Proof. We construct each integer digit-by-digit from the most significant position.
- Position: where is the number of digits of .
- Mask: tracks the parity of each digit’s occurrence count so far.
- Tight: A boolean indicating whether the prefix constructed so far matches 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 , choose digit where if tight, else .
- If not started and : proceed with mask unchanged, still not started.
- Otherwise: set , .
Base case: At , the number is a simber iff and .
Each integer corresponds to exactly one path through the DP. The started flag ensures is not counted (or counted separately if needed).
Lemma 1 (State Space Bound). The number of distinct DP states is at most .
Proof. There are positions, possible masks, 2 values for tight, and 2 values for started.
Lemma 2 (Simplification for ). When , we can separately count simbers with exactly digits for . For exactly digits, the leading digit and the remaining digits are in , with the constraint that the final mask is .
Proof. Since itself is not a simber (digit 1 appears once), counts simbers in , which is the union of simbers with digits for . For a fixed digit length, there is no upper-bound constraint (all -digit numbers are ), so the tight constraint is trivially released after the first digit, and we can use the combinatorial formula directly.
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 (from 1 to ), the DP processes positions with mask states and 10 digit choices per state. Total: . This can be optimized to by processing all digit lengths simultaneously.
- Space: for the DP table (using rolling arrays).
Answer
Code
Each problem page includes the exact C++ and Python source files from the local archive.
#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;
}
"""
Problem 520: Simbers
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.
"""
from functools import lru_cache
def count_simbers_up_to(N: int):
"""
Count simbers in [1, N] using digit DP.
Parity bitmask: bit i = 1 means digit i has appeared odd number of times.
A simber has mask = 0 at the end.
"""
digits = [int(d) for d in str(N)]
L = len(digits)
@lru_cache(maxsize=None)
def dp(pos, mask, tight, started):
if pos == L:
return 1 if (started and mask == 0) else 0
limit = digits[pos] if tight else 9
total = 0
for d in range(0, limit + 1):
new_tight = tight and (d == limit)
if not started and d == 0:
total += dp(pos + 1, mask, new_tight, False)
else:
new_mask = mask ^ (1 << d)
total += dp(pos + 1, new_mask, new_tight, True)
return total
result = dp(0, 0, True, False)
dp.cache_clear()
return result
def count_simbers_brute(N: int):
"""Brute-force for verification."""
count = 0
for n in range(1, N + 1):
s = str(n)
if all(s.count(d) % 2 == 0 for d in set(s)):
count += 1
return count
# Verify on small inputs
for N in [10, 100, 1000, 10000]:
dp_ans = count_simbers_up_to(N)
bf_ans = count_simbers_brute(N)
assert dp_ans == bf_ans, f"Mismatch at N={N}: {dp_ans} vs {bf_ans}"
print(f"Q(N={N}): {dp_ans}")
# Compute Q(10^n) for n = 1..15
print("\nQ(10^n) for various n:")
results = {}
for n in range(1, 16):
N = 10**n
q = count_simbers_up_to(N)
results[n] = q
print(f" Q(10^{n}) = {q}")