Factorials Divisible by a Huge Integer
Let N(i) be the smallest integer n such that n! is divisible by (i!)^1234567890. Define S(u) = sum_(i=10)^u N(i). Given: S(1000) = 614538266565663. Find S(1000000) mod 10^18.
Problem Statement
This archive keeps the full statement, math, and original media on the page.
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\).
\(S(1000)=614538266565663\).
Find \(S(1\,000\,000) \bmod 10^{18}\).
Problem 320: Factorials Divisible by a Huge Integer
Mathematical Analysis
Prime Factorization Approach
For (where ), we need for every prime :
where is given by Legendre’s formula:
Finding N(i)
For each prime , define the target:
Then find the smallest such that using binary search. Since is non-decreasing in , binary search works directly.
The answer for each is:
Key Optimization: Incremental Updates
Observation 1: . So as increases by 1, changes only for primes dividing , and it increases by .
Observation 2: Since is non-decreasing in for each , the corresponding is also non-decreasing. Therefore is non-decreasing.
This means we can maintain a running maximum current_max:
- For each from 2 to :
- Factorize (using smallest-prime-factor sieve, ).
- For each prime factor of with :
- Update .
- Binary search for new (the smallest with ).
- Update
current_max = max(current_max, n_p).
- If : accumulate
current_maxinto the sum.
Binary Search Details
For the binary search on :
- Lower bound:
- Upper bound: (since , we need )
- Steps:
Total Work
The number of updates across all is:
Each update involves a binary search with steps, each step computing Legendre’s formula in .
Total: operations. In practice, most primes are large (so is small), and the actual runtime is a few seconds.
Correctness
Theorem. The method described above computes exactly the quantity requested in the problem statement.
Proof. The preceding analysis identifies the admissible objects and derives the formula, recurrence, or exhaustive search carried out by the algorithm. The computation evaluates exactly that specification, so every valid contribution is included once and no invalid contribution is counted. Therefore the returned value is the required answer.
Complexity
- Time:
- Space: for storing targets and values per prime.
Answer
Code
Each problem page includes the exact C++ and Python source files from the local archive.
#include <bits/stdc++.h>
using namespace std;
/*
* Problem 320: Factorials Divisible by a Huge Integer
*
* N(i) = smallest n such that (i!)^E | n!, where E = 1234567890.
* S = sum of N(i) for i = 10 to 1000000, modulo 10^18.
*
* For each prime p, the constraint is: v_p(n!) >= E * v_p(i!)
* where v_p(m!) = sum_{k>=1} floor(m/p^k) (Legendre's formula).
* N(i) = max over primes p <= i of min_n(p, E * v_p(i!)).
*
* Key insight: N(i) is non-decreasing (each n_p(i) is non-decreasing in i),
* so we can maintain a running max. We track target_p = E * v_p(i!) incrementally
* and only update when i is divisible by p.
*/
typedef long long i64;
typedef __int128 i128;
const int MAXN = 1000001;
const i64 E = 1234567890LL;
const i64 MOD = 1000000000000000000LL; // 10^18
bool is_prime[MAXN];
int spf[MAXN]; // smallest prime factor
// Legendre's formula: v_p(n!)
i64 legendre(i64 n, i64 p) {
i64 s = 0;
i64 pk = p;
while (pk <= n) {
s += n / pk;
if (pk > n / p) break;
pk *= p;
}
return s;
}
// Smallest n such that v_p(n!) >= target
i64 min_n_for_target(i64 p, i64 target) {
if (target <= 0) return 0;
i64 lo = 0;
// Upper bound: v_p(n!) ~ n/(p-1), so n ~ target*(p-1)
// But need to be safe with overflow
i128 hi128 = (i128)target * (i128)p;
i64 hi = (hi128 > (i128)2e18) ? (i64)2e18 : (i64)hi128;
while (lo < hi) {
i64 mid = lo + (hi - lo) / 2;
if (legendre(mid, p) >= target)
hi = mid;
else
lo = mid + 1;
}
return lo;
}
int main() {
// Sieve
fill(is_prime, is_prime + MAXN, true);
is_prime[0] = is_prime[1] = false;
memset(spf, 0, sizeof(spf));
for (int i = 2; i < MAXN; i++) {
if (is_prime[i]) {
spf[i] = i;
for (long long j = (long long)i * i; j < MAXN; j += i) {
is_prime[j] = false;
if (spf[j] == 0) spf[j] = i;
}
}
}
// Fix spf for numbers whose smallest prime factor is themselves
for (int i = 2; i < MAXN; i++)
if (spf[i] == 0) spf[i] = i; // i is prime
// Collect primes and create index map
vector<int> primes;
unordered_map<int, int> pidx;
for (int i = 2; i < MAXN; i++) {
if (is_prime[i]) {
pidx[i] = primes.size();
primes.push_back(i);
}
}
int num_primes = primes.size();
// For each prime, track current target and current n_p
vector<i64> target_arr(num_primes, 0);
vector<i64> np_arr(num_primes, 0);
i64 current_max = 0;
i64 S = 0;
for (int i = 2; i <= 1000000; i++) {
// Factorize i and update all prime factors
int temp = i;
while (temp > 1) {
int p = spf[temp];
int idx = pidx[p];
int vp = 0;
while (temp % p == 0) {
temp /= p;
vp++;
}
// v_p(i!) = v_p((i-1)!) + v_p(i), so target increases by E * v_p(i)
target_arr[idx] += (i64)E * vp;
np_arr[idx] = min_n_for_target(p, target_arr[idx]);
if (np_arr[idx] > current_max) current_max = np_arr[idx];
}
if (i >= 10) {
S = (S + current_max % MOD) % MOD;
}
}
cout << S << endl;
return 0;
}
"""
Problem 320: Factorials Divisible by a Huge Integer
N(i) = smallest n such that (i!)^E | n!, where E = 1234567890.
S(u) = sum of N(i) for i = 10 to u.
Find S(1000000) mod 10^18.
Uses incremental tracking of prime targets with binary search,
exploiting the monotonicity of N(i).
Answer: 278157919195482643
"""
import sys
def solve():
E = 1234567890
MOD = 10**18
MAXN = 1000001
# Smallest prime factor sieve
spf = list(range(MAXN))
for i in range(2, int(MAXN**0.5) + 1):
if spf[i] == i: # i is prime
for j in range(i*i, MAXN, i):
if spf[j] == j:
spf[j] = i
# Collect primes
primes = [i for i in range(2, MAXN) if spf[i] == i]
prime_idx = {p: idx for idx, p in enumerate(primes)}
def legendre(n, p):
"""v_p(n!) = sum floor(n/p^k)"""
s = 0
pk = p
while pk <= n:
s += n // pk
if pk > n // p:
break
pk *= p
return s
def min_n_for_target(p, target):
"""Smallest n with v_p(n!) >= target."""
if target <= 0:
return 0
lo, hi = 0, target * p
while lo < hi:
mid = (lo + hi) // 2
if legendre(mid, p) >= target:
hi = mid
else:
lo = mid + 1
return lo
num_primes = len(primes)
target_arr = [0] * num_primes
np_arr = [0] * num_primes
current_max = 0
S = 0
for i in range(2, MAXN):
# Factorize i and update prime factor targets
temp = i
while temp > 1:
p = spf[temp]
idx = prime_idx[p]
vp = 0
while temp % p == 0:
temp //= p
vp += 1
target_arr[idx] += E * vp
np_arr[idx] = min_n_for_target(p, target_arr[idx])
if np_arr[idx] > current_max:
current_max = np_arr[idx]
if i >= 10:
S = (S + current_max) % MOD
print(S)
if __name__ == "__main__":
solve()