公式

E - 商品の逆元ポイント / Modular Inverse Points of Products 解説 by admin

Claude 4.6 Opus (Thinking)

Overview

This problem asks us to find the sum of the “modular multiplicative inverses (mod \(M\))” of the product of prices of selected items, over all combinations of choosing \(K\) items from \(N\) items. This can be reduced to computing the \(K\)-th elementary symmetric polynomial of the modular inverses of each item’s price.

Analysis

Key Insight 1: Handling multiples of \(M\)

Let \(B_i = A_i \mod M\). If an item with \(B_i = 0\) is selected, the product \(P\) of that combination is necessarily a multiple of \(M\), so the points are \(0\). Therefore, we only need to consider items where \(B_i \neq 0\).

Key Insight 2: Decomposition into product of inverses

Since \(M\) is prime, for subsets consisting only of items with \(B_i \neq 0\):

\[P^{-1} \equiv \prod_{i \in S} B_i^{-1} \pmod{M}\]

holds. In other words, “the inverse of the product” equals “the product of the inverses.”

Key Insight 3: Reduction to elementary symmetric polynomial

The desired value is:

\[\sum_{|S|=K} \prod_{i \in S} B_i^{-1} \pmod{M}\]

Letting \(c_i = B_i^{-1} \mod M\), this is nothing other than the \(K\)-th elementary symmetric polynomial \(e_K(c_1, \ldots, c_n)\) of \(c_1, c_2, \ldots, c_n\).

Problem with the naive approach

Enumerating all \(\binom{N}{K}\) combinations results in an astronomically large number when \(N=5000, K=2500\), making it completely infeasible.

Algorithm

Elementary symmetric polynomials can be efficiently computed using DP.

DP Definition: \(dp[j]\) = the sum of products of selected elements when choosing \(j\) elements from the elements seen so far (\(\mod M\))

Initial State: \(dp[0] = 1\), all others are \(0\)

Transition: When adding a new element \(x\) (update \(j\) from largest to smallest): $\(dp[j] \leftarrow dp[j] + dp[j-1] \cdot x \pmod{M}\)$

Updating \(j\) from largest to smallest prevents using the same element multiple times (a technique from the 0-1 knapsack problem).

Concrete Example: For \(c = [2, 3, 5]\), \(K=2\): - Initial: \(dp = [1, 0, 0]\) - After adding \(x=2\): \(dp = [1, 2, 0]\) - After adding \(x=3\): \(dp = [1, 5, 6]\) - After adding \(x=5\): \(dp = [1, 10, 31]\)

\(dp[2] = 31 = 2 \cdot 3 + 2 \cdot 5 + 3 \cdot 5\), confirming that the elementary symmetric polynomial is correctly computed.

Complexity

  • Time complexity: \(O(N \cdot K + N \log M)\)
    • DP updates: \(O(N \cdot K)\) (at most \(5000 \times 5000 = 25 \times 10^6\))
    • Computing the inverse of each element: \(O(N \log M)\) (using Fermat’s little theorem: \(\text{pow}(b, M-2, M)\))
  • Space complexity: \(O(N + K)\)

Implementation Notes

  • Computing inverses: Since \(M\) is prime, by Fermat’s little theorem we can compute \(b^{-1} \equiv b^{M-2} \pmod{M}\). Python’s pow(b, M-2, M) is fast.

  • DP update order: By updating \(j\) in decreasing order from \(K\) to \(1\), we prevent duplicate use of the same element. This is the same technique used in the 0-1 knapsack problem.

  • When non-zero elements are fewer than \(K\): If there are fewer than \(K\) items with \(B_i \neq 0\), it is impossible to select \(K\) items all with non-zero values, so the answer is \(0\).

    Source Code

import sys
from collections import Counter

def solve():
    input_data = sys.stdin.buffer.read().split()
    N = int(input_data[0])
    K = int(input_data[1])
    M = int(input_data[2])
    A = [int(input_data[3 + i]) for i in range(N)]
    
    # We need to compute sum over all K-subsets S of inv(prod(A_i for i in S)) mod M
    # where inv is modular inverse mod M, and if prod is 0 mod M, contribution is 0.
    
    # Let B_i = A_i mod M. Some B_i might be 0 (when A_i is divisible by M).
    # If B_i = 0, then any subset containing item i has product divisible by M, contributing 0.
    # So only subsets consisting entirely of items with B_i != 0 matter.
    
    # For items with B_i != 0, inv(prod) = prod(inv(B_i)).
    # So we need: sum over all K-subsets of non-zero items of prod(inv(B_i) for i in subset).
    
    # This is the K-th elementary symmetric polynomial of {inv(B_i)} for non-zero items.
    
    B = [a % M for a in A]
    
    # Separate zero and non-zero
    non_zero = [b for b in B if b != 0]
    n = len(non_zero)  # number of non-zero items
    
    # If n < K, all subsets either have fewer than K non-zero items or include zero items
    # Either way, answer is 0
    if n < K:
        print(0)
        return
    
    # Compute inv(b) for each non-zero b
    # inv(b) = pow(b, M-2, M)
    inv_vals = [pow(b, M - 2, M) for b in non_zero]
    
    # We need e_K(inv_vals) = sum of products of all K-subsets of inv_vals
    # This is the K-th elementary symmetric polynomial.
    
    # Standard DP: dp[j] = e_j after processing some elements
    # dp[0] = 1, and for each new element x, update dp[j] = dp[j] + dp[j-1]*x for j from K down to 1
    
    # n can be up to 5000, K up to 5000, so O(n*K) which is up to 25*10^6 - should be okay
    
    dp = [0] * (K + 1)
    dp[0] = 1
    
    for x in inv_vals:
        # Update from high to low to avoid using same element twice
        for j in range(min(K, dp.__len__() - 1), 0, -1):
            dp[j] = (dp[j] + dp[j - 1] * x) % M
    
    print(dp[K] % M)

solve()

This editorial was generated by claude4.6opus-thinking.

投稿日時:
最終更新: