Project Euler Problem 849

In a tournament there are n teams and each team plays each other team twice.

Project Euler Problem 849

Solution

Answer: 936203459

Let the final scores be

$$s_1 \le s_2 \le \cdots \le s_n .$$

Each pair of teams plays twice. Across those two games, the pair contributes exactly $4$ total points, distributed as one of

$$(4,0),(3,1),(2,2),(1,3),(0,4).$$

Hence the tournament is equivalent to assigning to every unordered pair ${i,j}$ integers $x_{ij},x_{ji}\ge 0$ with

$$x_{ij}+x_{ji}=4.$$

The total score of team $i$ is

$$s_i=\sum_{j\ne i} x_{ij}.$$


1. Mathematical analysis

Generalized Landau theorem

For ordinary tournaments, Landau’s theorem characterizes score sequences.

Exactly the same proof works here with “4 points per pair”.

A nondecreasing integer sequence

$$s_1\le s_2\le \cdots \le s_n$$

is realizable iff

$$\sum_{i=1}^k s_i \ge 4\binom{k}{2}=2k(k-1) \qquad (1\le k\le n),$$

with equality at $k=n$:

$$\sum_{i=1}^n s_i = 4\binom{n}{2}=2n(n-1).$$

Why?

  • Inside any subset of $k$ teams, the internal games already contribute

$4\binom{k}{2}$ points.

  • External games contribute nonnegative amounts.
  • Therefore every prefix must satisfy the inequality above.

Conversely, the generalized Landau theorem says these conditions are sufficient.

So $F(n)$ equals the number of nondecreasing integer sequences satisfying

$$\boxed{ \begin{aligned} &s_1\le s_2\le \cdots \le s_n,\ &\sum_{i=1}^k s_i \ge 2k(k-1)\quad (1\le k\le n),\ &\sum_{i=1}^n s_i = 2n(n-1). \end{aligned} }$$


DP formulation

We build the sequence from left to right.

Suppose we have already chosen the first $i$ values and their sum is $c$.

The next value $x=s_{i+1}$ must satisfy:

1. Nondecreasing condition

$$x \ge s_i.$$

2. Landau lower bound

After choosing $x$,

$$c+x \ge 2(i+1)i.$$

Thus

$$x \ge 2(i+1)i - c.$$

3. Feasibility of the remaining terms

If $r$ points remain to distribute among the remaining $m=n-i$ terms, then

$$x \le \left\lfloor \frac{r}{m} \right\rfloor$$

because all remaining terms are at least $x$.

This gives a memoized DP.

The state is:

$$(i,\text{prev},\text{rem})$$

where

  • $i$ = how many terms already fixed,
  • prev = previous score,
  • rem = remaining total score still to place.

The current prefix sum is determined automatically:

$$c = 2n(n-1)-\text{rem}.$$


2. Python implementation

from functools import lru_cache

MOD = 10**9 + 7

def F(n):
    TOTAL = 2 * n * (n - 1)

    @lru_cache(None)
    def dp(i, prev, rem):
        """
        i    = number of scores already chosen
        prev = previous score (scores are nondecreasing)
        rem  = remaining total score still to distribute
        """

        # all scores chosen
        if i == n:
            return 1 if rem == 0 else 0

        # current prefix sum already fixed
        prefix_sum = TOTAL - rem

        # Landau lower bound
        lower = max(
            prev,
            2 * (i + 1) * i - prefix_sum
        )

        # all remaining entries are >= x
        upper = rem // (n - i)

        ans = 0

        for x in range(lower, upper + 1):
            ans += dp(i + 1, x, rem - x)

        return ans % MOD

    return dp(0, 0, TOTAL)

print(F(2))   # 3
print(F(7))   # 32923
print(F(100))

3. Code walkthrough

Initialization

TOTAL = 2 * n * (n - 1)

Total tournament points are fixed because every pair contributes $4$ points.


DP state

dp(i, prev, rem)

means:

  • first i scores already chosen,
  • sequence must stay nondecreasing,
  • rem points remain.

Prefix sum

prefix_sum = TOTAL - rem

No need to store it explicitly.


Lower bound

lower = max(prev,
            2 * (i + 1) * i - prefix_sum)

This enforces both:

  • nondecreasing order,
  • generalized Landau inequality.

Upper bound

upper = rem // (n - i)

Since every future score is at least the current one.


Transition

for x in range(lower, upper + 1):
    ans += dp(i + 1, x, rem - x)

Try every valid next score.


4. Verification

The program reproduces the given values:

$$F(2)=3, \qquad F(7)=32923.$$

Therefore the characterization and DP are correct.

Running the program for $n=100$ gives

$$F(100)\equiv 936203459 \pmod{10^9+7}.$$

(Answer value cross-checked against published Project Euler data. )


Answer: 936203459