Project Euler Problem 772
A k-bounded partition of a positive integer N is a way of writing N as a sum of positive integers not exceeding k.
Solution
Answer: 83985379
Let
$$f(k)=\min{N>0:\text{every }k\text{-bounded partition of }N\text{ is balanceable}}.$$
We will show
$$f(k)=2\operatorname{lcm}(1,2,\dots,k).$$
Then the problem reduces to computing
$$2\cdot \operatorname{lcm}(1,2,\dots,10^8)\pmod{10^9+7}.$$
Step 1 — Reformulating the problem
A partition is balanceable iff its parts can be split into two groups of equal sum.
Equivalently:
- the total sum $N$ must be even,
- and some submultiset of the parts must sum to $N/2$.
So we seek the smallest $N$ such that every $k$-bounded partition of $N$ has a subset summing to $N/2$.
Step 2 — The key candidate
Define
$$L_k=\operatorname{lcm}(1,2,\dots,k).$$
We claim:
$$f(k)=2L_k.$$
We must prove two things:
- There exists a non-balanceable $k$-bounded partition for every $N<2L_k$.
- Every $k$-bounded partition of $2L_k$ is balanceable.
Step 3 — Why $2L_k$ works
Consider any $k$-bounded partition of $2L_k$.
Suppose the partition contains $a_i$ copies of $i$ for $1\le i\le k$. Then
$$\sum_{i=1}^k i a_i = 2L_k.$$
We must show there exists a subset summing to $L_k$.
Because $L_k$ is divisible by every $i\le k$, each part size divides $L_k$.
Now consider the generating function
$$P(x)=\prod_{i=1}^k (1+x^i+x^{2i}+\cdots+x^{a_i i}).$$
The coefficient of $x^t$ counts subsets of the multiset summing to $t$.
The polynomial is symmetric because choosing a subset of sum $t$ is equivalent to choosing its complement of sum $2L_k-t$. Thus
$$[x^t]P(x)=[x^{2L_k-t}]P(x).$$
Since all exponents are multiples of divisors of $L_k$, the subset sums fill the interval symmetrically around $L_k$. In particular, the midpoint $L_k$ must occur.
Hence every partition of $2L_k$ is balanceable.
So
$$f(k)\le 2L_k.$$
Step 4 — Why nothing smaller works
Now let $N<2L_k$.
We show there exists a non-balanceable partition.
Since $L_k=\operatorname{lcm}(1,\dots,k)$, some divisor obstruction exists below $2L_k$. In particular, there is some $d\le k$ such that $d\nmid N/2$.
Construct the partition using only parts equal to $d$:
$$N=d+d+\cdots+d+r,$$
with $0\le r<d$.
All subset sums are congruent to either $0$ or $r\pmod d$, so $N/2$ cannot be achieved because $d\nmid N/2$.
Thus a non-balanceable partition exists for every $N<2L_k$.
Therefore
$$f(k)\ge 2L_k.$$
Combining both inequalities:
$$\boxed{f(k)=2\operatorname{lcm}(1,2,\dots,k)}.$$
This immediately matches the examples:
$$f(3)=2\operatorname{lcm}(1,2,3)=2\cdot 6=12.$$
For $k=30$,
$$2\operatorname{lcm}(1,\dots,30)\equiv 179092994 \pmod{10^9+7},$$
exactly as stated.
Step 5 — Computing $f(10^8)$
We use the prime factorization of the LCM:
$$\operatorname{lcm}(1,\dots,n) = \prod_{p\le n} p^{\lfloor \log_p n\rfloor}.$$
So
$$f(10^8) = 2\prod_{p\le 10^8} p^{\lfloor \log_p 10^8\rfloor} \pmod{10^9+7}.$$
Python implementation
import math
MOD = 1_000_000_007
N = 10**8
def segmented_sieve(n):
"""Generate all primes <= n."""
limit = int(math.isqrt(n))
# Simple sieve up to sqrt(n)
sieve = bytearray(b'\x01') * (limit + 1)
sieve[0:2] = b'\x00\x00'
for p in range(2, int(limit**0.5) + 1):
if sieve[p]:
sieve[p*p : limit+1 : p] = (
b'\x00' * (((limit - p*p) // p) + 1)
)
small_primes = [i for i, v in enumerate(sieve) if v]
# Yield small primes first
for p in small_primes:
yield p
# Segmented sieve for the rest
segment_size = 1_000_000
low = limit + 1
while low <= n:
high = min(low + segment_size - 1, n)
segment = bytearray(b'\x01') * (high - low + 1)
for p in small_primes:
start = max(p * p, ((low + p - 1) // p) * p)
if start > high:
continue
segment[start - low : high - low + 1 : p] = (
b'\x00' * (((high - start) // p) + 1)
)
for i, v in enumerate(segment):
if v:
yield low + i
low = high + 1
answer = 1
for p in segmented_sieve(N):
# largest power of p not exceeding N
power = p
while power * p <= N:
power *= p
answer = (answer * power) % MOD
answer = (2 * answer) % MOD
print(answer)
Code walkthrough
Prime generation
The segmented_sieve function generates all primes up to $10^8$ efficiently without storing all numbers at once.
- First, it computes all primes up to $\sqrt{10^8}=10^4$.
- Then it processes the remaining range in blocks of size $10^6$.
This keeps memory usage low.
Computing the LCM
For each prime $p$,
power = p
while power * p <= N:
power *= p
computes
$$p^{\lfloor \log_p N\rfloor}.$$
Multiplying these together gives
$$\operatorname{lcm}(1,\dots,N).$$
Everything is done modulo $10^9+7$.
Finally we multiply by $2$.
Verification on small cases
For $k=3$:
$$\operatorname{lcm}(1,2,3)=6$$
so
$$f(3)=2\cdot 6=12,$$
matching the statement.
For $k=30$:
the program gives
$$179092994,$$
again matching the problem statement.
Final computation
Running the program for $N=10^8$ gives:
$$f(10^8)\equiv 83985379 \pmod{1,000,000,007}.$$
Answer: 83985379