CF 104728F - 新取模运算

We are given a prime number $p$, and many queries. Each query provides a huge integer $n$, and we need to evaluate a custom operation on $n!$.

CF 104728F - \u65b0\u53d6\u6a21\u8fd0\u7b97

Rating: -
Tags: -
Solve time: 1m 35s
Verified: no

Solution

Problem Understanding

We are given a prime number $p$, and many queries. Each query provides a huge integer $n$, and we need to evaluate a custom operation on $n!$.

The operation $x \oplus p$ behaves like taking $x \bmod p$, but with a twist: before taking the remainder, we repeatedly divide $x$ by $p$ as long as it is divisible by $p$. In other words, all factors of $p$ are stripped out completely before computing the remainder modulo $p$.

A more algebraic way to see this is that if we write

$$x = p^k \cdot r \quad \text{where } p \nmid r,$$

then $x \oplus p = r \bmod p$. Since $r$ is not divisible by $p$, this is just $r \bmod p$, but $r$ itself may still be large.

Our task is to compute this value for $x = n!$, for each query independently.

The constraints make direct computation of factorial impossible. With $n$ up to $10^{18}$, even iterating up to $n$ is out of the question. A single factorial computation already exceeds any feasible time bound, and there are up to $10^5$ queries, so even logarithmic work per query must be extremely efficient.

A naive approach would attempt to compute $n! \bmod p$, then divide out factors of $p$, but that still implicitly requires handling all numbers up to $n$, which is impossible.

A subtle edge case appears when $n < p$. In this case, $n!$ contains no factor of $p$, so the operation reduces to simply $n! \bmod p$. A careless solution that always applies a “remove $p$-factors” recurrence without treating this base case correctly may introduce incorrect recursion depth or unnecessary modular exponentiation.

Another tricky situation is when $n$ is very large but has a small quotient $n // p$. The structure of the solution depends heavily on this quotient decomposition, and failing to recognize this leads to exponential behavior if one tries to simulate factorial structure directly.

Approaches

A brute-force approach would compute $n!$ directly and then repeatedly divide by $p$ while possible, finally taking modulo $p$. This is correct in principle, because it follows the definition of the operation exactly. However, computing $n!$ already requires $O(n)$ multiplications, which is impossible for $n$ up to $10^{18}$. Even for a single query, this is infeasible.

The key observation is that we never need the full factorial, only its value modulo $p$ after removing all factors of $p$. This suggests separating contributions of numbers divisible by $p$ and those not divisible by $p$. Since $p$ is prime, the structure of multiples of $p$ repeats regularly, allowing a divide-and-conquer decomposition on base $p$.

We split numbers from $1$ to $n$ into full blocks of size $p$ and a remainder block. Each full block contributes a structured factor that can be simplified using Wilson’s theorem, where $(p-1)! \equiv -1 \pmod p$. This converts each full block into a simple multiplicative contribution, and the remaining part is a smaller factorial problem. This leads to a recursive reduction from $n$ to $n // p$, giving a logarithmic depth.

Approach Time Complexity Space Complexity Verdict
Brute Force $O(n)$ per query $O(1)$ Too slow
Optimal $O(\log_p n + p)$ preprocessing $O(p)$ Accepted

Algorithm Walkthrough

We define a function $F(n)$ as the value of $n!$ after removing all factors of $p$, taken modulo $p$.

Precomputation

  1. Compute factorials modulo $p$ for all values from $0$ to $p-1$. This gives direct access to $(n \bmod p)!$ whenever we need it.

Recursive computation

  1. If $n = 0$, return $1$, since empty product contributes neutrally.
  2. Split $n$ into full blocks of size $p$: let $n = a \cdot p + b$, where $b = n \bmod p$. This separates numbers into complete cycles and a partial tail.
  3. The tail contributes $(b!)$, which we already know from precomputation.
  4. Each full block from $1$ to $p$ contributes $(p-1)! \equiv -1 \pmod p$ after removing the factor $p$. This is where the primality of $p$ is essential.
  5. Therefore, all full blocks contribute $(-1)^a$, since there are $a$ such blocks.
  6. We recursively compute the contribution from $a!$, because the structure repeats at scale $p$. This gives the factor $F(a)$.
  7. Combine everything:

$$F(n) = F(a) \cdot (-1)^a \cdot (b!) \bmod p.$$

Why it works

The invariant is that at every recursive level, we factor out contributions of numbers grouped by residue classes modulo $p$. Each group of size $p$ behaves identically after removing multiples of $p$, and reduces to a constant value modulo $p$. The recursion tracks how many full groups exist at each scale, and Wilson’s theorem ensures those groups collapse into a simple sign factor. Because each level reduces $n$ to $n // p$, no contribution is lost, and all factors of $p$ are consistently removed before modular reduction.

Python Solution

import sys
input = sys.stdin.readline

def solve():
    T, p = map(int, input().split())

    fact = [1] * p
    for i in range(1, p):
        fact[i] = fact[i - 1] * i % p

    def F(n):
        if n == 0:
            return 1
        a, b = divmod(n, p)
        res = F(a)
        if a % 2:
            res = (res * (p - 1)) % p
        return res * fact[b] % p

    for _ in range(T):
        n = int(input())
        print(F(n))

if __name__ == "__main__":
    solve()

The solution first precomputes factorials modulo $p$, which allows constant-time lookup for any remainder part. The recursive function implements the decomposition $n = a p + b$, ensuring that each level removes one digit of $n$ in base $p$. The multiplication by $p-1$ handles the $(-1)^a$ term without branching exponentiation.

The recursion depth is at most $O(\log_p n)$, which is safe even for $n = 10^{18}$.

Worked Examples

We trace the computation using two small illustrative inputs.

Example 1

Let $p = 5$, $n = 12$.

n a = n//p b = n%p F(a) contribution from a fact[b] result
12 2 2 F(2)=2 (-1)^2 = 1 2 4

Here $12! = 479001600$. After removing all factors of 5 and reducing modulo 5, the result is consistent with the computed value.

This trace shows how the problem reduces from 12 to 2 in a single step, demonstrating the logarithmic collapse of the factorial structure.

Example 2

Let $p = 7$, $n = 20$.

n a = n//p b = n%p F(a) contribution from a fact[b] result
20 2 6 F(2)=2 (-1)^2 = 1 6! mod 7 = 6 5

This demonstrates how the remainder factorial and recursive structure interact independently. The full-block contribution depends only on parity of the quotient.

Complexity Analysis

Measure Complexity Explanation
Time $O(T \log_p n + p)$ Each query reduces $n$ by factor $p$, factorial precomputation costs $O(p)$
Space $O(p)$ Stores factorial modulo $p$

The constraints allow up to $10^5$ queries, but each query only performs logarithmic recursion depth with respect to base $p$. Even in worst cases, this remains efficient under the given limits.

Test Cases

import sys, io

def run(inp: str) -> str:
    sys.stdin = io.StringIO(inp)
    from math import factorial

    # placeholder: assume solve() is defined above
    # return output string
    return "NOT IMPLEMENTED"

# sample cases (structure only)
# assert run("...") == "..."

# custom edge cases
assert True
Test input Expected output What it validates
small n < p n! mod p base case correctness
n = p (p-1)! mod p Wilson boundary
large n computed value recursion correctness
n = 1e18 valid output depth handling

Edge Cases

When $n < p$, the recursion immediately stops at the factorial lookup table. For example, if $n = 4$ and $p = 11$, the function directly returns $4!$, since no division by $p$ occurs.

When $n = p$, the decomposition gives $a = 1, b = 0$. The algorithm returns $F(1) \cdot (p-1) \cdot 1$, which matches the fact that $p! = p \cdot (p-1)!$, and after removing $p$, we are left with $(p-1)! \equiv -1 \pmod p$, consistent with the recurrence structure.

For very large $n$, repeated division by $p$ guarantees that the recursion terminates in logarithmic depth, and no intermediate value exceeds modular bounds.