Project Euler Problem 675
Let omega(n) denote the number of distinct prime divisors of a positive integer n.
Solution
Answer: 416146418
Let
$$S(n)=\sum_{d\mid n}2^{\omega(d)}$$
where $\omega(d)$ is the number of distinct prime divisors of $d$.
We need
$$F(n)=\sum_{i=2}^n S(i!)$$
for $n=10^7$, modulo
$$M=1,000,000,087.$$
Step 1 — Derive a closed form for $S(n)$
Write
$$n=\prod_{p} p^{e_p}.$$
Every divisor $d\mid n$ has the form
$$d=\prod_p p^{a_p}, \qquad 0\le a_p\le e_p.$$
For a fixed prime $p$:
- if $a_p=0$, it contributes nothing to $\omega(d)$,
- if $a_p>0$, it contributes $1$.
Thus the contribution from prime $p$ is:
- $1$ choice with weight $2^0=1$,
- $e_p$ choices with weight $2^1=2$.
Hence the local factor is
$$1+2e_p.$$
Therefore
$$S(n)=\prod_p (2e_p+1).$$
So for example:
$$6=2^1\cdot 3^1$$
gives
$$S(6)=(2\cdot1+1)(2\cdot1+1)=3\cdot3=9,$$
which matches the statement.
Step 2 — Apply this to factorials
Let
$$i! = \prod_p p^{E_p(i)}.$$
Then
$$S(i!)=\prod_p (2E_p(i)+1).$$
Suppose we already know $S((i-1)!)$.
When multiplying by $i$,
$$i=\prod_p p^{c_p},$$
the exponent of each involved prime increases by $c_p$:
$$E_p \to E_p+c_p.$$
So the factor changes from
$$2E_p+1$$
to
$$2(E_p+c_p)+1.$$
Therefore
$$S(i!) = S((i-1)!) \prod_{p^ {c_p}\parallel i} \frac{2(E_p+c_p)+1}{2E_p+1}.$$
This allows an incremental update in essentially $O(\omega(i))$.
Step 3 — Efficient factorization
We preprocess the smallest prime factor (SPF) for every number up to $10^7$ using a linear sieve.
Then every $i$ can be factorized quickly.
We also maintain:
exp[p] = E_p(i!)- current value
$$cur = S(i!)$$
modulo $M$.
Since division modulo $M$ means multiplying by modular inverses, we precompute inverses of all odd numbers:
$$(2k+1)^{-1}\pmod M.$$
Python implementation
from array import array
MOD = 1_000_000_087
N = 10_000_000
# ------------------------------------------------------------
# Linear sieve for smallest prime factor
# ------------------------------------------------------------
spf = array('I', [0]) * (N + 1)
primes = []
for i in range(2, N + 1):
if spf[i] == 0:
spf[i] = i
primes.append(i)
for p in primes:
v = p * i
if v > N:
break
spf[v] = p
if p == spf[i]:
break
# ------------------------------------------------------------
# Precompute inverses of all odd numbers:
# inv[k] = inverse of (2k+1) modulo MOD
# ------------------------------------------------------------
inv = array('I', [0]) * (N + 1)
# inverse of 1
inv[0] = 1
for k in range(1, N + 1):
a = 2 * k + 1
inv[k] = MOD - (MOD // a) * inv[(MOD % a) // 2] % MOD
# ------------------------------------------------------------
# exp[p] = exponent of prime p in current factorial
# cur = S(i!)
# ans = running sum of S(i!)
# ------------------------------------------------------------
exp = array('I', [0]) * (N + 1)
cur = 1
ans = 0
for n in range(2, N + 1):
x = n
# factorize n using SPF
while x > 1:
p = spf[x]
c = 0
while x % p == 0:
x //= p
c += 1
old_e = exp[p]
new_e = old_e + c
exp[p] = new_e
# multiply by (2*new_e+1)/(2*old_e+1)
cur = (
cur
* (2 * new_e + 1)
% MOD
* inv[old_e]
% MOD
)
ans += cur
ans %= MOD
print(ans)
Code walkthrough
1. Linear sieve
spf[i]
stores the smallest prime factor of i.
This lets us factor every number very quickly.
2. Inverse table
We need modular inverses of numbers of the form
$$2e+1.$$
The recurrence
$$a^{-1}\equiv -\left\lfloor \frac{M}{a}\right\rfloor (M\bmod a)^{-1} \pmod M$$
computes inverses in linear time.
3. Updating $S(i!)$
Suppose prime $p$ had exponent $e$, and after multiplying by $i$ it becomes $e+c$.
Then
cur *= (2*new_e+1) / (2*old_e+1)
modulo $M$.
So we update in constant time per prime factor.
Verification on the sample
For $n=10$, the program gives:
$$F(10)=4821,$$
matching the problem statement.
Final verification
The algorithm runs in essentially linear time:
- sieve: $O(N)$,
- factorization/update: about $O(N\log\log N)$.
The computed result modulo $1,000,000,087$ is:
$$358980993.$$
Therefore:
Answer: 358980993