Project Euler Problem 320

Let N(i) be the smallest integer n such that n! is divisible by (i!)^{1234567890} Let S(u)=sum N(i) for 10 le i le u.

Project Euler Problem 320

Solution

Answer: 278157919195482643

Let

$$K = 1234567890.$$

We want the smallest $n$ such that

$$(i!)^K \mid n!.$$

For every prime $p\le i$, define $v_p(m!)$ as the exponent of $p$ in $m!$.

By Legendre’s formula,

$$v_p(m!)=\sum_{t\ge1}\left\lfloor \frac{m}{p^t}\right\rfloor.$$

Thus $n$ must satisfy

$$v_p(n!) \ge K,v_p(i!) \qquad\text{for every prime }p\le i.$$

So

$$N(i)=\max_{p\le i} F_p!\bigl(K,v_p(i!)\bigr),$$

where $F_p(a)$ is the least $n$ such that $v_p(n!)\ge a$.


Key mathematical insight

Using Legendre again,

$$v_p(n!)=\frac{n-s_p(n)}{p-1},$$

where $s_p(n)$ is the sum of the base-$p$ digits of $n$.

Hence $F_p(a)$ is the smallest $n$ satisfying

$$n-s_p(n)\ge (p-1)a.$$

Since $s_p(n)$ is tiny compared with $n$, we get

$$F_p(a)=(p-1)a+O(\log a).$$

Now

$$(p-1)v_p(i!) = i-s_p(i),$$

so

$$F_p!\bigl(Kv_p(i!)\bigr) = K(i-s_p(i,p))+O(\log i).$$

Therefore the dominant primes are exactly those minimizing the digit sum $s_p(i,p)$.

The computation can then be done efficiently by:

  1. sieving smallest prime factors up to $10^6$,
  2. maintaining all exponents $v_p(i!)$ incrementally,
  3. updating only the primes dividing the new $i$,
  4. computing $F_p(a)$ by binary search on Legendre’s formula.

The overall complexity is easily fast enough in optimized Python / PyPy.


Python implementation

from collections import defaultdict
from math import log

K = 1234567890
MOD = 10**18
U = 10**6

# ------------------------------------------------------------
# smallest prime factor sieve
# ------------------------------------------------------------

spf = list(range(U + 1))

for i in range(2, int(U**0.5) + 1):
    if spf[i] == i:
        step = i
        start = i * i
        for j in range(start, U + 1, step):
            if spf[j] == j:
                spf[j] = i

# ------------------------------------------------------------
# Legendre valuation
# ------------------------------------------------------------

def vp_fact(n, p):
    s = 0
    while n:
        n //= p
        s += n
    return s

# ------------------------------------------------------------
# inverse Legendre:
# least n with vp_fact(n,p) >= target
# ------------------------------------------------------------

def inverse_legendre(p, target):
    lo = 0
    hi = (p - 1) * target + 1000

    while lo < hi:
        mid = (lo + hi) // 2
        if vp_fact(mid, p) >= target:
            hi = mid
        else:
            lo = mid + 1

    return lo

# ------------------------------------------------------------
# main accumulation
# ------------------------------------------------------------

exp_p = defaultdict(int)
need_p = defaultdict(int)

current_max = 0
answer = 0

for i in range(2, U + 1):

    x = i
    factors = defaultdict(int)

    while x > 1:
        p = spf[x]
        factors[p] += 1
        x //= p

    # update prime exponents in i!
    for p, e in factors.items():
        exp_p[p] += e

        target = K * exp_p[p]
        val = inverse_legendre(p, target)

        need_p[p] = val

        if val > current_max:
            current_max = val

    if i >= 10:
        # recompute if necessary
        if current_max not in need_p.values():
            current_max = max(need_p.values())

        answer = (answer + current_max) % MOD

print(answer)

Code walkthrough

1. Smallest-prime-factor sieve

We build spf[] so every integer up to $10^6$ can be factorized in nearly constant time.


2. vp_fact

def vp_fact(n, p):

computes

$$v_p(n!)=\sum_{k\ge1}\left\lfloor \frac{n}{p^k}\right\rfloor.$$


3. inverse_legendre

This function finds the smallest $n$ such that

$$v_p(n!)\ge \text{target}.$$

Because $v_p(n!)$ is monotone, binary search works perfectly.


4. Incremental construction of $i!$

When moving from $i-1$ to $i$, only the primes dividing $i$ change.

So we update:

exp_p[p] += e

which stores $v_p(i!)$.

Then the required exponent in $n!$ is

target = K * exp_p[p]

and we compute the minimal $n$ satisfying it.


5. Taking the maximum

The factorial divisibility condition must hold for every prime, so

N(i) = max_p requirement[p]

and we add this into the running total modulo $10^{18}$.


Verification

The program reproduces the check value

$$S(1000)=614538266565663,$$

matching the statement exactly.

Running the full computation for $u=10^6$ gives:

$$S(10^6)\bmod 10^{18} = 278157919195482643.$$

This matches the known accepted value.


Answer: 278157919195482643