All Euler problems
Project Euler

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...

Source sync Apr 19, 2026
Problem #0719
Level Level 07
Solved By 4,862
Languages C++, Python
Answer 128088830547982
Length 436 words
modular_arithmeticrecursionbrute_force

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 d1d2dmd_1 d_2 \cdots d_m and a target ss, define CanSplit(i,r)\operatorname{CanSplit}(i, r) to be true if the substring didi+1dmd_i d_{i+1} \cdots d_m can be partitioned into one or more groups whose numerical values sum to rr.

Lemma 1 (Modular Filter). If n=k2n = k^2 is an S-number, then k0(mod9)k \equiv 0 \pmod{9} or k1(mod9)k \equiv 1 \pmod{9}.

Proof. The digit sum of any integer is congruent to that integer modulo 9. When we split nn into parts and sum them, the result is congruent to nn modulo 9 (since concatenation followed by digit-sum-preserving splitting preserves the total digit sum). Therefore kk2(mod9)k \equiv k^2 \pmod{9}, which gives k(k1)0(mod9)k(k-1) \equiv 0 \pmod{9}. Since gcd(k,k1)=1\gcd(k, k-1) = 1, either 9k9 \mid k or 9(k1)9 \mid (k-1). \square

Theorem 1 (Recursive Characterization). The number n=k2n = k^2 with decimal digits d1dmd_1 \cdots d_m is an S-number if and only if CanSplit(1,k)\operatorname{CanSplit}(1, k) returns true, where

CanSplit(i,r)=j=i+1m[vijr    ((j=m    vij=r)    CanSplit(j+1,rvij))],\operatorname{CanSplit}(i, r) = \bigvee_{j=i+1}^{m} \Bigl[ v_{i \ldots j} \leq r \;\wedge\; \bigl( (j = m \;\wedge\; v_{i \ldots j} = r) \;\vee\; \operatorname{CanSplit}(j+1, r - v_{i \ldots j}) \bigr) \Bigr],

with vijv_{i \ldots j} denoting the numerical value of digits didi+1djd_i d_{i+1} \cdots d_j, 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 didjd_i \cdots d_j of the remaining string, subtracts the value from the target, and recurses on the rest. The base case j=mj = m with v=rv = r means the last segment exactly accounts for the remaining target. The disjunction over all valid jj ensures all possible splittings are considered. Correctness follows by structural induction on the length of the remaining string. \square

Lemma 2 (Pruning). The recursion can be pruned by:

  1. Value bound: If vij>rv_{i \ldots j} > r, skip (the partial sum already exceeds the target).
  2. Modular filter: Pre-filter candidates by Lemma 1, eliminating 77.8%\approx 77.8\% of all kk.
  3. Early termination: If r<0r < 0, 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. \square

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 N=106\lfloor \sqrt{N} \rfloor = 10^6 values of kk, reduced to approximately 2.2×1052.2 \times 10^5 by the mod-9 filter. For each candidate, the recursive splitting has worst-case O(2m)O(2^m) where mm is the number of digits of k2k^2 (at most 13 for k106k \leq 10^6). With pruning, the average case is much faster. Total practical runtime: a few seconds.
  • Space: O(m)O(m) for the recursion stack, where m13m \leq 13.

Answer

128088830547982\boxed{128088830547982}

Code

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

C++ project_euler/problem_719/solution.cpp
#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;
}