Open Source · 7 Languages · 900+ Problems

Project Euler Solutions

Complete solutions in C++, Python & Java — with step-by-step mathematical explanations

All Problems

Problem 310: Nim Square

View on Project Euler

Project Euler Problem 310 Solution

EulerSolve provides an optimized solution for Project Euler Problem 310, Nim Square, with C++, Python, Java, and a step-by-step mathematical explanation.

Problem Summary In Nim Square, a move on one heap of size \(n\) removes any positive square \(k^2\le n\). The position has three heaps, and we must count how many ordered-by-size triples $0\le a\le b\le c\le N$ are losing positions. Mathematical Approach 1) Single-heap Grundy numbers For one heap, the legal moves are $n\to n-1,\ n-4,\ n-9,\dots$ for every square not exceeding \(n\). So the Grundy value of a single heap is $g(0)=0,$ $g(n)=\operatorname{mex}\{g(n-k^2):k^2\le n\}.$ This is the usual subtract-a-square recurrence. 2) Why three heaps reduce to xor The full game is the disjoint sum of three impartial subgames. Therefore Sprague-Grundy theory says that the triple \((a,b,c)\) is losing exactly when the nim-sum vanishes: $g(a)\oplus g(b)\oplus g(c)=0.$ So once the array \(g(0),\dots,g(N)\) is known, the entire counting problem becomes a combinatorial xor-counting problem. 3) Small worked example The first few single-heap values are $g(0..4)=(0,1,0,1,2).$ For instance: $g(1)=\operatorname{mex}\{g(0)\}=\operatorname{mex}\{0\}=1,$ $g(2)=\operatorname{mex}\{g(1)\}=\operatorname{mex}\{1\}=0,$ $g(4)=\operatorname{mex}\{g(3),g(0)\}=\operatorname{mex}\{1,0\}=2.$ If \(N=4\), the losing triples are $\begin{aligned} &(0,0,0),(0,0,2),(0,1,1),(0,1,3),(0,2,2),(0,3,3),\\ &(0,4,4),(1,1,2),(1,2,3),(2,2,2),(2,3,3),(2,4,4), \end{aligned}$ so the count is \(12\)....

Detailed mathematical approach

Problem Summary

In Nim Square, a move on one heap of size \(n\) removes any positive square \(k^2\le n\). The position has three heaps, and we must count how many ordered-by-size triples

$0\le a\le b\le c\le N$

are losing positions.

Mathematical Approach

1) Single-heap Grundy numbers

For one heap, the legal moves are

$n\to n-1,\ n-4,\ n-9,\dots$

for every square not exceeding \(n\). So the Grundy value of a single heap is

$g(0)=0,$

$g(n)=\operatorname{mex}\{g(n-k^2):k^2\le n\}.$

This is the usual subtract-a-square recurrence.

2) Why three heaps reduce to xor

The full game is the disjoint sum of three impartial subgames. Therefore Sprague-Grundy theory says that the triple \((a,b,c)\) is losing exactly when the nim-sum vanishes:

$g(a)\oplus g(b)\oplus g(c)=0.$

So once the array \(g(0),\dots,g(N)\) is known, the entire counting problem becomes a combinatorial xor-counting problem.

3) Small worked example

The first few single-heap values are

$g(0..4)=(0,1,0,1,2).$

For instance:

$g(1)=\operatorname{mex}\{g(0)\}=\operatorname{mex}\{0\}=1,$

$g(2)=\operatorname{mex}\{g(1)\}=\operatorname{mex}\{1\}=0,$

$g(4)=\operatorname{mex}\{g(3),g(0)\}=\operatorname{mex}\{1,0\}=2.$

If \(N=4\), the losing triples are

$\begin{aligned} &(0,0,0),(0,0,2),(0,1,1),(0,1,3),(0,2,2),(0,3,3),\\ &(0,4,4),(1,1,2),(1,2,3),(2,2,2),(2,3,3),(2,4,4), \end{aligned}$

so the count is \(12\).

4) The naive count is cubic

A direct scan over all triples \((a,b,c)\) would take

$O(N^3)$

time, which is impossible for \(N=100000\). The code instead reorganizes the count by fixing the middle heap \(b\).

5) Prefix/suffix counting for a fixed middle heap

Fix \(b\). Then:

Left side: \(a\) may range over \(0\le a\le b\).

Right side: \(c\) may range over \(b\le c\le N\).

Let

$\text{pref}[x]=\#\{a\le b:g(a)=x\},$

$\text{suf}[y]=\#\{c\ge b:g(c)=y\}.$

For a fixed Grundy value \(x=g(a)\), the losing condition

$x\oplus g(b)\oplus g(c)=0$

forces

$g(c)=x\oplus g(b).$

Hence the number of losing triples with this middle value \(b\) is

$\text{add}_b=\sum_x \text{pref}[x]\cdot \text{suf}[x\oplus g(b)].$

Summing \(\text{add}_b\) over all \(b\) gives the final answer.

6) Why this automatically respects \(a\le b\le c\)

The implementation maintains the arrays in a very specific order:

Before counting for \(b\): it adds \(g(b)\) to the prefix array.

During counting: prefix therefore represents \(a\in[0,b]\), while suffix still represents \(c\in[b,N]\).

After counting: it removes \(g(b)\) from suffix, so the next step uses \(c\ge b+1\).

This means the ordering constraint is built directly into the data structure update order; no extra combinatorial correction is needed.

How the Code Works

1) Build Grundy table. build_grundy(limit) computes the subtract-a-square Grundy values using a timestamp-based mex array.

2) Determine Grundy alphabet size. The code records

$\max g(n)$

so it knows how large the prefix and suffix frequency arrays must be.

3) Initialize suffix. Initially all heaps \(0,\dots,N\) are available on the right, so suffix counts every Grundy value in the full range.

4) Sweep the middle index. For each \(b\), the code updates prefix, applies the xor-count formula above, then removes \(b\) from suffix.

5) Checkpoints. The C++ version verifies

$\texttt{solve(29)}=1160$

and also checks that \(\texttt{solve(40)}\) matches a literal brute-force triple scan.

6) Final Project Euler value. For the required bound \(N=100000\), the implementation returns

$2586528661783.$

Complexity Analysis

Computing the Grundy table costs about \(O(N\sqrt N)\), because each \(g(n)\) examines all squares up to \(n\). The counting sweep costs \(O(N\cdot G)\), where \(G\) is the number of distinct Grundy values encountered, which stays small in practice. Memory usage is \(O(N+G)\).

Further Reading

  1. Problem page: https://projecteuler.net/problem=310
  2. Sprague-Grundy theorem: https://en.wikipedia.org/wiki/Sprague%E2%80%93Grundy_theorem
  3. Subtract-a-square game: https://en.wikipedia.org/wiki/Subtract_a_square

Mathematical approach · C++ solution · Python solution · Java solution

Previous: Problem 309 · All Project Euler solutions · Next: Problem 311

Need help with a problem? Ask me! 💡
e
✦ Euler GLM 5.2
Hi! I'm Euler. Ask me anything about Project Euler problems, math concepts, or solution approaches! 🧮