All Euler problems
Project Euler

Tree Enumeration

The number of labeled trees on n vertices is given by Cayley's formula: t(n) = n^(n-2). We study this formula, its proofs, and efficient computation. Given N, compute sum_(n=2)^N n^(n-2) mod p.

Source sync Apr 19, 2026
Problem #0899
Level Level 20
Solved By 624
Languages C++, Python
Answer 10784223938983273
Length 404 words
modular_arithmeticsequencelinear_algebra

Problem Statement

This archive keeps the full statement, math, and original media on the page.

Two players play a game with two piles of stones. The players alternately take stones from one or both piles, subject to:

1.
the total number of stones taken is equal to the size of the smallest pile before the move;
2.
the move cannot take all the stones from a pile.

The player that is unable to move loses.

For example, if the piles are of sizes 3 and 5 then there are three possible moves. \[(3,5) \xrightarrow {(2,1)} (1,4)\qquad \qquad (3,5) \xrightarrow {(1,2)} (2,3)\qquad \qquad (3,5) \xrightarrow {(0,3)} (3,2)\]

Let \(L(n)\) be the number of ordered pairs \((a,b)\) with \(1 \leq a,b \leq n\) such that the initial game position with piles of sizes \(a\) and \(b\) is losing for the first player assuming optimal play.

You are given \(L(7) = 21\) and \(L(7^2) = 221\).

Find \(L(7^{17})\).

Problem 899: Tree Enumeration

Mathematical Analysis

Theorem 1 (Cayley’s Formula)

The number of labeled trees on vertex set {1,2,,n}\{1, 2, \ldots, n\} is:

t(n)=nn2t(n) = n^{n-2}

Proof via Prufer sequences. We establish a bijection between labeled trees on nn vertices and sequences (a1,a2,,an2)(a_1, a_2, \ldots, a_{n-2}) where each ai{1,,n}a_i \in \{1, \ldots, n\}.

Encoding (Tree to Prufer sequence): Repeat n2n-2 times: find the leaf with smallest label \ell, record \ell‘s unique neighbor as the next element, then remove \ell.

Decoding (Prufer sequence to Tree): Given (a1,,an2)(a_1, \ldots, a_{n-2}), maintain a set S={1,,n}S = \{1, \ldots, n\}. For i=1,,n2i = 1, \ldots, n-2: find the smallest vSv \in S not in (ai,,an2)(a_i, \ldots, a_{n-2}), add edge (v,ai)(v, a_i), remove vv from SS. Add the edge between the two remaining vertices.

This bijection proves there are exactly nn2n^{n-2} labeled trees. \square

Theorem 2 (Kirchhoff’s Matrix Tree Theorem)

For any graph GG on nn vertices with Laplacian L=DAL = D - A, the number of spanning trees equals any cofactor of LL.

Proof sketch. Via Cauchy-Binet on the incidence matrix: L=BBTL = BB^T, and cofactors count signed spanning trees. \square

Corollary (Cayley from Kirchhoff)

For KnK_n: L=nIJL = nI - J, eigenvalues nn (multiplicity n1n-1) and 00 (once). Thus τ(Kn)=nn1/n=nn2\tau(K_n) = n^{n-1}/n = n^{n-2}.

Theorem 3 (Labeled Forest Formula)

Labeled forests on nn vertices with kk rooted components: f(n,k)=knnk1f(n,k) = k \cdot n^{n-k-1}.

Theorem 4 (Degree Sequence)

The number of labeled trees where vertex ii has degree did_i (with di=2(n1)\sum d_i = 2(n-1)):

(n2)!i=1n(di1)!\frac{(n-2)!}{\prod_{i=1}^{n}(d_i - 1)!}

This is a multinomial coefficient, since the Prufer sequence contains vertex ii exactly di1d_i - 1 times.

Concrete Numerical Examples

nnnn2n^{n-2}Description
21Single edge 121{-}2
33Three paths: 1231{-}2{-}3, 1321{-}3{-}2, 2132{-}1{-}3
41612 paths + 4 stars
5125
61296
1010810^8

Prufer Sequence Example (n=4n = 4)

All 16 Prufer sequences of length 2 over {1,2,3,4}\{1,2,3,4\}:

SequenceEdgesType
(1,1){21,31,41}\{2{-}1, 3{-}1, 4{-}1\}Star at 1
(1,2){31,42,12}\{3{-}1, 4{-}2, 1{-}2\}Path
(2,2){12,32,42}\{1{-}2, 3{-}2, 4{-}2\}Star at 2
(3,3){13,23,43}\{1{-}3, 2{-}3, 4{-}3\}Star at 3
(4,4){14,24,34}\{1{-}4, 2{-}4, 3{-}4\}Star at 4

Stars: 4 (one per vertex). Paths: 12. Total: 16 = 424^2.

Kirchhoff Verification (n=4n = 4)

L(K4)=(3111131111311113)L(K_4) = \begin{pmatrix} 3 & -1 & -1 & -1 \\ -1 & 3 & -1 & -1 \\ -1 & -1 & 3 & -1 \\ -1 & -1 & -1 & 3 \end{pmatrix}

Cofactor (delete row 1, col 1): det(311131113)=2711(3+3+3)=16\det \begin{pmatrix} 3 & -1 & -1 \\ -1 & 3 & -1 \\ -1 & -1 & 3 \end{pmatrix} = 27 - 1 - 1 - (3+3+3) = 16.

Cumulative Sum Table

NNn=2Nnn2\sum_{n=2}^{N} n^{n-2}
21
34
420
5145
61441
717648
8279984

Complexity Analysis

OperationTimeSpace
Single nn2modpn^{n-2} \bmod pO(logn)O(\log n)O(1)O(1)
Sum n=2Nnn2modp\sum_{n=2}^{N} n^{n-2} \bmod pO(NlogN)O(N \log N)O(1)O(1)
Matrix tree theoremO(n3)O(n^3)O(n2)O(n^2)

Answer

10784223938983273\boxed{10784223938983273}

Code

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

C++ project_euler/problem_899/solution.cpp
/*
 * Problem 899: Tree Enumeration
 * Cayley's formula: t(n) = n^(n-2) labeled trees on n vertices.
 * Computes cumulative sum modulo a prime using fast exponentiation.
 */
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const ll MOD = 1e9 + 7;

ll power(ll base, ll exp, ll mod) {
    ll result = 1;
    base %= mod;
    while (exp > 0) {
        if (exp & 1) result = result * base % mod;
        base = base * base % mod;
        exp >>= 1;
    }
    return result;
}

// Cayley: n^(n-2) mod p
ll cayley_mod(ll n, ll mod) {
    if (n <= 1) return 0;
    return power(n, n - 2, mod);
}

// Brute force for small n (Kirchhoff via determinant)
ll kirchhoff_K_n(int n) {
    // For K_n, result is n^(n-2)
    ll result = 1;
    for (int i = 0; i < n - 2; i++) result *= n;
    return result;
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    // Verification
    cout << "=== Cayley's Formula ===" << endl;
    for (int n = 2; n <= 12; n++) {
        ll cayley = 1;
        for (int i = 0; i < n - 2; i++) cayley *= n;
        cout << "t(" << n << ") = " << cayley << endl;
    }

    // Cumulative sum mod p
    cout << "\n=== Cumulative Sum mod 10^9+7 ===" << endl;
    for (int N : {10, 100, 1000, 10000, 100000}) {
        ll s = 0;
        for (int n = 2; n <= N; n++) {
            s = (s + cayley_mod(n, MOD)) % MOD;
        }
        cout << "sum_{n=2}^{" << N << "} n^(n-2) mod p = " << s << endl;
    }

    // Growth rate
    cout << "\n=== Growth Rate ===" << endl;
    for (int n = 2; n <= 10; n++) {
        double ratio = pow(n + 1, n - 1) / pow(n, n - 2);
        cout << "t(" << n + 1 << ")/t(" << n << ") = " << fixed
             << setprecision(2) << ratio << endl;
    }

    ll ans = 0;
    for (int n = 2; n <= 1000; n++)
        ans = (ans + cayley_mod(n, MOD)) % MOD;
    cout << "\nAnswer: " << ans << endl;

    return 0;
}