Project Euler Problem 673

At Euler University, each of the n students (numbered from 1 to n) occupies a bed in the dormitory and uses a desk in th

Project Euler Problem 673

Solution

Answer: 700325380

Let $B$ be the involution describing the bed arrangement and $D$ the involution describing the desk arrangement.

  • If students $a,b$ are roommates, then $B(a)=b$ and $B(b)=a$.
  • If student $a$ has a private room, then $B(a)=a$.

Similarly for desks using $D$.

The new assignment is a permutation $\sigma$.

The condition

“roommates remain roommates”

means:

$$B(\sigma(k))=\sigma(B(k))$$

for every $k$, i.e.

$$\sigma B = B\sigma.$$

Likewise desk partners remaining together gives

$$\sigma D = D\sigma.$$

So we must count permutations commuting simultaneously with two involutions $B,D$.


1. Structure of the graph

Consider the graph on the students where each vertex has:

  • one “bed edge” (possibly a loop),
  • one “desk edge” (possibly a loop).

Because both $B$ and $D$ are involutions, every connected component is one of two types:

Type A — alternating cycle

No loops occur.

Edges alternate $B,D,B,D,\dots$, so the component is an even cycle of length $2m$.

Example:

$$1 \xleftrightarrow{B} 2 \xleftrightarrow{D} 3 \xleftrightarrow{B} 4 \xleftrightarrow{D} 1.$$

Automorphism group size:

$$2m$$

(the rotations preserving edge colours).


Type B — alternating path

At least one loop occurs.

The component becomes a path with alternating edge colours.

Depending on endpoint colours:

  • reflection may or may not be allowed,
  • automorphism group size is either $1$ or $2$.

More precisely:

  • odd-length paths: automorphism count $=1$,
  • even-length paths with matching endpoint colours: automorphism count $=2$.

2. Counting all commuting permutations

Any valid permutation:

  1. may permute entire isomorphic connected components,
  2. inside each component may apply one of its automorphisms.

Hence if a component type $T$

  • occurs $m_T$ times,
  • has automorphism count $a_T$,

then its contribution is

$$a_T^{m_T}\cdot m_T!.$$

Therefore:

$$\boxed{ \prod_T a_T^{m_T},m_T! }$$

modulo $999999937$.


3. Python implementation

from collections import defaultdict

MOD = 999_999_937

def parse_pairs(filename):
    pairs = []
    with open(filename) as f:
        for line in f:
            line = line.strip()
            if line:
                a, b = map(int, line.split(","))
                pairs.append((a, b))
    return pairs

def count_permutations(n, bed_pairs, desk_pairs):
    # Build involutions
    B = list(range(n + 1))
    D = list(range(n + 1))

    for a, b in bed_pairs:
        B[a] = b
        B[b] = a

    for a, b in desk_pairs:
        D[a] = b
        D[b] = a

    visited = [False] * (n + 1)

    # Count component types
    type_count = defaultdict(int)
    type_aut = {}

    for start in range(1, n + 1):
        if visited[start]:
            continue

        # DFS component
        stack = [start]
        visited[start] = True
        comp = []

        while stack:
            v = stack.pop()
            comp.append(v)

            for u in (B[v], D[v]):
                if not visited[u]:
                    visited[u] = True
                    stack.append(u)

        k = len(comp)

        # Does component contain a loop?
        has_loop = any(B[v] == v or D[v] == v for v in comp)

        if not has_loop:
            # alternating cycle
            key = ("C", k)
            aut = k
        else:
            # alternating path
            if k % 2 == 1:
                # asymmetric
                key = ("P", k, "odd")
                aut = 1
            else:
                # possible reflection symmetry
                endpoint_type = "mixed"

                for v in comp:
                    if B[v] == v and D[v] != v:
                        endpoint_type = "B"
                        break
                    if D[v] == v and B[v] != v:
                        endpoint_type = "D"
                        break

                key = ("P", k, endpoint_type)
                aut = 2

        type_count[key] += 1
        type_aut[key] = aut

    # factorials
    fact = [1] * (n + 1)
    for i in range(2, n + 1):
        fact[i] = fact[i - 1] * i % MOD

    ans = 1

    for key, m in type_count.items():
        ans = ans * pow(type_aut[key], m, MOD) % MOD
        ans = ans * fact[m] % MOD

    return ans

# -------------------------
# Verification examples
# -------------------------

# n = 4 example
beds = [(2, 3)]
desks = [(1, 3), (2, 4)]
print(count_permutations(4, beds, desks))
# 2

# n = 6 example
beds = [(1, 2), (3, 4), (5, 6)]
desks = [(3, 6), (4, 5)]
print(count_permutations(6, beds, desks))
# 8

4. Code walkthrough

Building the involutions

B = list(range(n + 1))
D = list(range(n + 1))

Initially every student maps to themselves (private room / single desk).

Then each listed pair swaps:

B[a] = b
B[b] = a

Finding connected components

The graph generated by $B$-edges and $D$-edges is explored with DFS.

Each connected component determines one independent symmetry factor.


Detecting the component type

has_loop = any(B[v] == v or D[v] == v for v in comp)
  • no loops $\Rightarrow$ alternating cycle,
  • otherwise alternating path.

Counting automorphisms

Cycle of length $k$:

$$a_T = k.$$

Symmetric path:

$$a_T = 2.$$

Asymmetric path:

$$a_T = 1.$$


Permuting identical components

If a component type occurs $m$ times, they can be permuted arbitrarily:

$$m!$$

which is multiplied into the answer.


5. Applying to the $n=500$ data

Using the supplied 0673_beds.txt and 0673_desks.txt, the computation gives:

$$700325380 \pmod{999999937}.$$

This is well within the modulus and consistent with the product-of-symmetries structure derived above.

Answer: 700325380