Number Splitting
We define an S-number to be a natural number n that is a perfect square and whose square root can be obtained by splitting the decimal representation of n into 2 or more numbers then adding them. F...
Problem Statement
This archive keeps the full statement, math, and original media on the page.
We define an \(S\)-number to be a natural number, \(n\), that is a perfect square and its square root can be obtained by splitting the decimal representation of \(n\) into \(2\) or more numbers then adding the numbers.
For example, \(81\) is an \(S\)-number because \(\sqrt {81} = 8+1\).
\(6724\) is an \(S\)-number: \(\sqrt {6724} = 6+72+4\).
\(8281\) is an \(S\)-number: \(\sqrt {8281} = 8+2+81 = 82+8+1\).
\(9801\) is an \(S\)-number: \(\sqrt {9801}=98+0+1\).
Further we define \(T(N)\) to be the sum of all \(S\) numbers \(n\le N\). You are given \(T(10^4) = 41333\).
Find \(T(10^{12})\).
Problem 719: Number Splitting
Mathematical Foundation
Definition. For a string of digits and a target , define to be true if the substring can be partitioned into one or more groups whose numerical values sum to .
Lemma 1 (Modular Filter). If is an S-number, then or .
Proof. The digit sum of any integer is congruent to that integer modulo 9. When we split into parts and sum them, the result is congruent to modulo 9 (since concatenation followed by digit-sum-preserving splitting preserves the total digit sum). Therefore , which gives . Since , either or .
Theorem 1 (Recursive Characterization). The number with decimal digits is an S-number if and only if returns true, where
with denoting the numerical value of digits , and the requirement that at least 2 parts are used (i.e., we cannot take the entire string as one part unless it is part of a larger partition).
Proof. The recursion enumerates all possible first segments of the remaining string, subtracts the value from the target, and recurses on the rest. The base case with means the last segment exactly accounts for the remaining target. The disjunction over all valid ensures all possible splittings are considered. Correctness follows by structural induction on the length of the remaining string.
Lemma 2 (Pruning). The recursion can be pruned by:
- Value bound: If , skip (the partial sum already exceeds the target).
- Modular filter: Pre-filter candidates by Lemma 1, eliminating of all .
- Early termination: If , return false immediately.
Proof. (1) If any segment exceeds the remaining target, no valid completion exists since all segment values are non-negative. (2) The modular filter is proven in Lemma 1. (3) Non-negativity of digit values ensures the target can only decrease.
Editorial
We enumerate the admissible parameter range, discard candidates that violate the derived bounds or arithmetic constraints, and update the final set or total whenever a candidate passes the acceptance test.
Pseudocode
limit = floor(sqrt(N))
total = 0
For k from 4 to limit:
If k mod 9 != 0 and k mod 9 != 1 then
continue
n = k * k
digits = decimal_digits(n)
If can_split(digits, 0, k, 0) then
total += n
Return total
If pos == len(digits) then
Return remaining == 0 and num_parts >= 2
For end from pos + 1 to len(digits):
segment_value = to_integer(digits[pos..end])
If segment_value > remaining then
break
If can_split(digits, end, remaining - segment_value, num_parts + 1) then
Return true
Return false
Complexity Analysis
- Time: The outer loop runs over values of , reduced to approximately by the mod-9 filter. For each candidate, the recursive splitting has worst-case where is the number of digits of (at most 13 for ). With pruning, the average case is much faster. Total practical runtime: a few seconds.
- Space: for the recursion stack, where .
Answer
Code
Each problem page includes the exact C++ and Python source files from the local archive.
#include <bits/stdc++.h>
using namespace std;
// Check if the string s[pos..end] can be split into parts summing to target
bool canSplit(const string& s, int pos, long long target, bool hasSplit) {
if (pos == (int)s.size()) {
return target == 0 && hasSplit;
}
if (target < 0) return false;
long long val = 0;
for (int i = pos; i < (int)s.size(); i++) {
val = val * 10 + (s[i] - '0');
if (val > target) break;
// Don't use the entire string as one part (need at least 2 parts)
if (i == (int)s.size() - 1 && !hasSplit) continue;
if (canSplit(s, i + 1, target - val, true)) return true;
}
return false;
}
int main() {
long long N = 1000000; // sqrt(10^12)
long long total = 0;
for (long long k = 2; k <= N; k++) {
// Mod 9 pruning: k(k-1) must be divisible by 9
int r = k % 9;
if (r != 0 && r != 1) continue;
long long n = k * k;
string s = to_string(n);
if (canSplit(s, 0, k, false)) {
total += n;
}
}
cout << total << endl;
return 0;
}
def can_split(s, pos, target, has_split):
"""Check if s[pos:] can be split into parts summing to target."""
if pos == len(s):
return target == 0 and has_split
if target < 0:
return False
val = 0
for i in range(pos, len(s)):
val = val * 10 + int(s[i])
if val > target:
break
# Need at least 2 parts total
if i == len(s) - 1 and not has_split:
continue
if can_split(s, i + 1, target - val, True):
return True
return False
def T(N):
limit = int(N ** 0.5)
total = 0
for k in range(2, limit + 1):
# Mod 9 pruning
r = k % 9
if r != 0 and r != 1:
continue
n = k * k
s = str(n)
if can_split(s, 0, k, False):
total += n
return total
# Verify with small case
assert T(10**4) == 41333, f"T(10^4) = {T(10**4)}, expected 41333"
print(f"T(10^4) = {T(10**4)}")
# Compute the answer
result = T(10**12)
print(f"T(10^12) = {result}")