All Euler problems
Project Euler

The Locked Box

Consider n locked boxes, each requiring a specific key. You have m available keys, and each key opens a specific subset of boxes. Determine the minimum number of keys needed to open all n boxes, or...

Source sync Apr 19, 2026
Problem #0849
Level Level 30
Solved By 291
Languages C++, Python
Answer 936203459
Length 466 words
modular_arithmeticdynamic_programmingprobability

Problem Statement

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

In a tournament there are \(n\) teams and each team plays each other team twice. A team gets two points for a win, one point for a draw and no points for a loss.

With two teams there are three possible outcomes for the total points. \((4,0)\) where a team wins twice, \((3,1)\) where a team wins and draws, and \((2,2)\) where either there are two draws or a team wins one game and loses the other. Here we do not distinguish the teams and so \((3,1)\) and \((1,3)\) are considered identical.

Let \(F(n)\) be the total number of possible final outcomes with \(n\) teams, so that \(F(2) = 3\).

You are also given \(F(7) = 32923\).

Find \(F(100)\). Give your answer modulo \(10^9+7\).

Problem 849: The Locked Box

Mathematical Analysis

Coupon Collector’s Problem

Theorem. The expected number of trials to collect all nn distinct coupons when each trial yields a uniformly random coupon is:

E[T]=nHn=nk=1n1k(1)E[T] = n \cdot H_n = n \sum_{k=1}^{n} \frac{1}{k} \tag{1}

where HnH_n is the nn-th harmonic number.

Proof. Divide the collection into phases. Phase ii starts when we have i1i-1 distinct coupons and ends when we get the ii-th new one. The probability of getting a new coupon in phase ii is (ni+1)/n(n - i + 1)/n, so phase ii is geometric with expected length n/(ni+1)n/(n - i + 1). Summing:

E[T]=i=1nnni+1=nk=1n1k=nHn.E[T] = \sum_{i=1}^{n} \frac{n}{n - i + 1} = n \sum_{k=1}^{n} \frac{1}{k} = n H_n. \quad \square

Variance

Theorem. The variance of TT is:

Var(T)=n2k=1n1k2nHnπ2n26(2)\text{Var}(T) = n^2 \sum_{k=1}^{n} \frac{1}{k^2} - n H_n \approx \frac{\pi^2 n^2}{6} \tag{2}

Asymptotics

Theorem. As nn \to \infty:

E[T]=nlnn+γn+12+O(1/n)(3)E[T] = n \ln n + \gamma n + \frac{1}{2} + O(1/n) \tag{3}

where γ0.5772\gamma \approx 0.5772 is the Euler-Mascheroni constant.

Set Cover (NP-Hard General Case)

Theorem. The minimum set cover problem is NP-hard. The greedy algorithm (always pick the set covering the most uncovered elements) achieves an approximation ratio of Hn=O(lnn)H_n = O(\ln n), which is optimal unless P = NP.

DP for Exact Set Cover

For small nn, use bitmask DP: dp[S]\text{dp}[S] = minimum keys to cover set SS. Transition:

dp[SKj]=min(dp[S]+cj)\text{dp}[S \cup K_j] = \min(\text{dp}[S] + c_j)

over all keys jj with cost cjc_j covering KjK_j.

Concrete Examples

nnE[T]=nHnE[T] = nH_nnHnnH_n decimalVariance
111.0000
233.0001
511.41711.4178.694
1029.29029.29035.424
52235.978235.978(deck of cards)
100518.738518.7381064.8

Verification for n=2n=2: E[T]=2(1+1/2)=3E[T] = 2(1 + 1/2) = 3. Indeed: first draw always gives a new coupon. Second coupon has probability 1/2 each draw, expected 2 more draws. Total = 1+2=31 + 2 = 3. Correct.

Complexity Analysis

  • Coupon collector formula: O(n)O(n) for computing the harmonic sum.
  • Set cover greedy: O(mn)O(mn) where mm = number of keys.
  • Exact DP: O(2nm)O(2^n \cdot m) time, O(2n)O(2^n) space.

Markov Chain Formulation

The coupon collector process is a Markov chain on states {0,1,,n}\{0, 1, \ldots, n\} (number of distinct coupons collected). Transition probabilities: P(ii+1)=(ni)/nP(i \to i+1) = (n-i)/n and P(ii)=i/nP(i \to i) = i/n.

Theorem (Hitting Time Distribution). The probability that exactly TT trials are needed is:

P(T=t)=n!ntS(t,n)P(T = t) = \frac{n!}{n^t} S(t, n)

where S(t,n)S(t, n) is the Stirling number of the second kind (number of surjections from [t][t] to [n][n] divided by n!n!… actually, using inclusion-exclusion):

P(Tt)=j=0n(1)j(nj)(njn)tP(T \le t) = \sum_{j=0}^{n} (-1)^j \binom{n}{j} \left(\frac{n-j}{n}\right)^t

Birthday Problem Connection

The coupon collector is the “dual” of the birthday problem. Birthday: how many draws until a collision? Coupon: how many draws until full coverage? Both involve random sampling with replacement.

Theorem (Birthday). The expected number of draws for the first collision among nn types is approximately πn/2\sqrt{\pi n / 2}.

Double Dixie Cup Problem

Generalization. The double dixie cup problem asks: how many draws to get each coupon at least cc times?

E[Tc]=nk=1n1k+(c1)nlnn+O(n)E[T_c] = n \sum_{k=1}^{n} \frac{1}{k} + (c-1) n \ln n + O(n)

For c=2c = 2: E[T2]nlnn+nlnlnn+E[T_2] \approx n \ln n + n \ln\ln n + \cdots

Tail Bounds

Theorem. P(T>nlnn+cn)ecP(T > n \ln n + cn) \le e^{-c} for c>0c > 0. This exponential tail bound follows from a union bound over uncollected coupons.

Answer

936203459\boxed{936203459}

Code

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

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

ll modinv(ll a, ll mod = MOD) { return power(a, mod - 2, mod); }

// Coupon collector: E[T] = n * H_n mod p
ll coupon_collector_mod(int n) {
    ll hn = 0;
    for (int k = 1; k <= n; k++)
        hn = (hn + modinv(k)) % MOD;
    return (ll)n % MOD * hn % MOD;
}

// Set cover via bitmask DP
int min_set_cover(int n, const vector<int>& masks) {
    int full = (1 << n) - 1;
    vector<int> dp(full + 1, n + 1);
    dp[0] = 0;
    for (int s = 0; s <= full; s++) {
        if (dp[s] > n) continue;
        for (int mask : masks) {
            int ns = s | mask;
            dp[ns] = min(dp[ns], dp[s] + 1);
        }
    }
    return dp[full];
}

int main() {
    // Verify E[T] for n=2 is 3
    // H_2 = 1 + 1/2 = 3/2, so 2 * 3/2 = 3
    ll h2 = (1 + modinv(2)) % MOD;
    assert(2 * h2 % MOD == 3);

    // Set cover: 3 elements, 3 sets
    vector<int> masks = {0b011, 0b110, 0b101};
    assert(min_set_cover(3, masks) == 2);

    cout << coupon_collector_mod(1000) << endl;
    return 0;
}