Open Source · 7 Languages · 900+ Problems

Project Euler Solutions

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

All Problems

Problem 337: Totient Stairstep Sequences

View on Project Euler

Project Euler Problem 337 Solution

EulerSolve provides an optimized solution for Project Euler Problem 337, Totient Stairstep Sequences, with C++, Python, Java, and a step-by-step mathematical explanation.

Problem Summary A totient stairstep sequence is a strictly increasing integer sequence \(a_1,\dots,a_n\) such that \(a_1=6\) and, for every adjacent pair, $\varphi(a_i) \lt \varphi(a_{i+1}) \lt a_i \lt a_{i+1}.$ The first inequality forces the totient values to rise, the middle inequality says the next totient must still stay below the previous raw integer, and the last inequality keeps the sequence increasing. We must count all such sequences whose final term is at most \(N\), and report the answer modulo \(10^8\). The final Project Euler numeric answer is intentionally omitted. Mathematical Approach Formal DP State Let \(dp[x]\) be the number of valid sequences whose last term is exactly \(x\). The one-term sequence \([6]\) is valid, so $dp[6]=1.$ For \(x \ge 7\), every valid sequence ending at \(x\) must come from a previous endpoint \(i \lt x\) satisfying the step condition $\varphi(i) \lt \varphi(x) \lt i.$ Therefore $dp[x]=\sum_{i=6}^{x-1} dp[i]\,\mathbf{1}_{\varphi(i) \lt \varphi(x) \lt i}.$ The required total is then $S(N)=\sum_{x=6}^{N} dp[x]\pmod{10^8}.$ This recurrence already captures the full combinatorics of the problem, but evaluating it literally would compare each \(x\) against all smaller \(i\), leading to quadratic time. Why a Fixed Predecessor Creates an Interval Fix a predecessor \(i\)....

Detailed mathematical approach

Problem Summary

A totient stairstep sequence is a strictly increasing integer sequence \(a_1,\dots,a_n\) such that \(a_1=6\) and, for every adjacent pair,

$\varphi(a_i) \lt \varphi(a_{i+1}) \lt a_i \lt a_{i+1}.$

The first inequality forces the totient values to rise, the middle inequality says the next totient must still stay below the previous raw integer, and the last inequality keeps the sequence increasing. We must count all such sequences whose final term is at most \(N\), and report the answer modulo \(10^8\). The final Project Euler numeric answer is intentionally omitted.

Mathematical Approach

Formal DP State

Let \(dp[x]\) be the number of valid sequences whose last term is exactly \(x\). The one-term sequence \([6]\) is valid, so

$dp[6]=1.$

For \(x \ge 7\), every valid sequence ending at \(x\) must come from a previous endpoint \(i \lt x\) satisfying the step condition

$\varphi(i) \lt \varphi(x) \lt i.$

Therefore

$dp[x]=\sum_{i=6}^{x-1} dp[i]\,\mathbf{1}_{\varphi(i) \lt \varphi(x) \lt i}.$

The required total is then

$S(N)=\sum_{x=6}^{N} dp[x]\pmod{10^8}.$

This recurrence already captures the full combinatorics of the problem, but evaluating it literally would compare each \(x\) against all smaller \(i\), leading to quadratic time.

Why a Fixed Predecessor Creates an Interval

Fix a predecessor \(i\). The condition for a future value \(x\) is

$\varphi(i) \lt \varphi(x) \lt i,$

which depends on \(x\) only through the single value \(\varphi(x)\). Since totients are integers, this is equivalent to

$\varphi(x)\in[\varphi(i)+1,\; i-1].$

So once \(dp[i]\) is known, the same weight \(dp[i]\) should be added to every future number whose totient falls inside that interval. This observation eliminates the need to inspect every predecessor separately. It also shows immediately why prime endpoints are dead ends: if \(i\) is prime, then \(\varphi(i)=i-1\), so the interval is empty.

Accumulator on the Totient Axis

After all endpoints smaller than \(x\) have been processed, define

$A_x(t)=\sum_{i=6}^{x-1} dp[i]\,\mathbf{1}_{\varphi(i)+1\le t \le i-1}.$

This means \(A_x(t)\) stores the total contribution of all earlier endpoints that would accept a future number whose totient equals \(t\). Evaluating at \(t=\varphi(x)\) gives

$dp[x]=A_x(\varphi(x)).$

So the problem becomes: maintain many interval additions on the totient axis, and answer one point query for each \(x\). Because \(\varphi(x)\le x-1\) for every \(x \gt 1\), a Fenwick structure of size \(N\) is sufficient.

Fenwick Tree as a Difference Array

The implementation uses a Fenwick tree, not for direct prefix sums of \(dp\), but for the difference array of the accumulator \(A_x\). If we want to add a value \(v\) to every index in an interval \([L,R]\), we can update a difference array \(D\) by

$D[L]\mathrel{+}=v,\qquad D[R+1]\mathrel{-}=v.$

Then the actual value at position \(t\) is the prefix sum

$A_x(t)=\sum_{u=1}^{t} D[u].$

A Fenwick tree supports those point updates and prefix-sum queries in \(O(\log N)\). In other words, every finished state \(i\) performs one interval update on \([\varphi(i)+1,\; i-1]\), and every candidate endpoint \(x\) performs one point query at \(\varphi(x)\).

Totient Precomputation

The DP cannot even start before all values \(\varphi(1),\dots,\varphi(N)\) are known. These are computed with the standard totient sieve. Initialize \(\varphi[m]=m\) for all \(m\), and for every prime \(p\), visit all multiples \(kp\) and apply

$\varphi(kp)\leftarrow \varphi(kp)-\frac{\varphi(kp)}{p}.$

This is the batch version of the multiplicative formula \(\varphi(n)=n\prod_{p\mid n}(1-\frac1p)\). The asymptotic cost is \(O(N\log\log N)\). The C++ implementation uses block partitioning and optional multithreading for faster preprocessing, but the mathematical result is the same as the ordinary sieve.

Worked Example: \(N=10\)

The relevant totient values are

$\varphi(6)=2,\quad \varphi(7)=6,\quad \varphi(8)=4,\quad \varphi(9)=6,\quad \varphi(10)=4.$

Start with \(dp[6]=1\), so the total already contains the sequence \(\{6\}\). Since a successor of 6 must satisfy \(2 \lt \varphi(x) \lt 6\), we seed the interval \([3,5]\) with weight 1.

Now iterate upward:

\(x=7\): query \(\varphi(7)=6\), obtain \(dp[7]=0\), so no valid sequence ends at 7.

\(x=8\): query \(\varphi(8)=4\), obtain \(dp[8]=1\), corresponding to \(\{6,8\}\). Then 8 contributes to \([\varphi(8)+1,7]=[5,7]\).

\(x=9\): query \(\varphi(9)=6\), obtain \(dp[9]=1\), corresponding to \(\{6,8,9\}\).

\(x=10\): query \(\varphi(10)=4\), obtain \(dp[10]=1\), corresponding to \(\{6,10\}\).

Hence

$S(10)=1+1+1+1=4,$

namely the sequences \(\{6\}\), \(\{6,8\}\), \(\{6,8,9\}\), and \(\{6,10\}\).

Why the Code Starts with a Single Range Update

The code sets total = 1 for the trivial sequence \([6]\), then immediately performs

rangeAdd(phi[6] + 1, 5, 1).

Because \(\varphi(6)=2\), this is exactly the interval \([3,5]\), which represents every totient value allowed for a direct successor of 6. After that, each loop iteration follows the same pattern:

1. Query the current accumulator at \(\varphi(x)\) to obtain \(dp[x]\).

2. Add \(dp[x]\) to the running total.

3. Propagate \(dp[x]\) to the interval \([\varphi(x)+1,\; x-1]\) for future successors.

If the interval is empty, the helper simply skips the update.

How the Code Works

The C++ solution follows the derivation above literally. It first computes all totients, then scans \(x\) from 7 to \(N\). Each iteration performs one Fenwick point query and one range update, both reduced modulo \(10^8\). The C++ file also includes a brute-force checker for small \(N\) and validates the official checkpoints \(S(10)=4\), \(S(100)=482073668\), and \(S(10000)\bmod 10^8 = 73808307\). The Java version implements the same algorithm directly. The Python file is a thin bridge that compiles and runs the C++ solver so all three language entries stay consistent.

Complexity Analysis

The totient sieve costs \(O(N\log\log N)\). The DP stage performs \(N-6\) point queries and at most \(N-6\) interval updates, each in \(O(\log N)\), so the dominant cost is \(O(N\log N)\). Memory usage is \(O(N)\) for the \(\varphi\) array and the Fenwick structure. This is a dramatic improvement over the naive DP, which would require \(O(N^2)\) predecessor checks.

Footnotes and References

  1. Problem page: https://projecteuler.net/problem=337
  2. Euler totient function: https://en.wikipedia.org/wiki/Euler%27s_totient_function
  3. Fenwick tree (Binary Indexed Tree): https://en.wikipedia.org/wiki/Fenwick_tree
  4. Sieve of Eratosthenes and sieve-style preprocessing: https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes

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

Previous: Problem 336 · All Project Euler solutions · Next: Problem 338

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! 🧮