make-groups
Category
Miscellaneous
Points
199
Tags
So we got 2 files, calc.py
and chall.txt
,
calc.py
:
f = [x.strip() for x in open("chall.txt").read().split('\n')]
n = int(f[0])
a = list(map(int, f[1].split()))
m = 998244353
def factorial(n):
if n==0: return 1
if n==1: return 1
return n * factorial(n-1)
def choose(n, r):
return (factorial(n) // (factorial(r) * factorial(n-r))) % m
ans = 1
for x in a:
ans *= choose(n, x)
ans %= m
print(f"tjctf{{{ans}}}")%
and for the chall.txt, it just digusting random
number (jk)
So, this challenge requires calculating a product of combinations under a prime modulus, $m=998244353$
the calc.py
has two critical, first the recursive factorial
function is slow and will cause a RecursionError
for the large n
found in the challenge file, and has incorrect modulat arithmetic.
For the solution,
instead of re-calculating factorials, it precomputes all values up to n!
and stores them.
Crucially, it also precomputes the modular multiplicative inverse of each factorial. This is done efficiently by using Fermat Little Theorem
and use correct modular combinations.
solver.py
:
MOD = 998244353
def precompute_factorials(n, mod):
fact = [1] * (n + 1)
inv_fact = [1] * (n + 1)
for i in range(1, n + 1):
fact[i] = fact[i - 1] * i % mod
# Fermat's Little Theorem for inverse factorial
inv_fact[n] = pow(fact[n], mod - 2, mod)
for i in range(n - 1, -1, -1):
inv_fact[i] = inv_fact[i + 1] * (i + 1) % mod
return fact, inv_fact
def comb(n, r, fact, inv_fact, mod):
if r < 0 or r > n:
return 0
return fact[n] * inv_fact[r] % mod * inv_fact[n - r] % mod
with open("chall.txt") as f:
lines = [x.strip() for x in f]
n = int(lines[0])
a = list(map(int, lines[1].split()))
fact, inv_fact = precompute_factorials(n, MOD)
ans = 1
for x in a:
ans = ans * comb(n, x, fact, inv_fact, MOD) % MOD
print(f"tjctf{{{ans}}}")
make-groups Flag:
tjctf{148042038}