結果
問題 |
No.1039 Project Euler でやれ
|
ユーザー |
![]() |
提出日時 | 2025-05-14 13:01:25 |
言語 | PyPy3 (7.3.15) |
結果 |
WA
|
実行時間 | - |
コード長 | 9,227 bytes |
コンパイル時間 | 337 ms |
コンパイル使用メモリ | 81,896 KB |
実行使用メモリ | 86,360 KB |
最終ジャッジ日時 | 2025-05-14 13:03:40 |
合計ジャッジ時間 | 2,185 ms |
ジャッジサーバーID (参考情報) |
judge1 / judge4 |
(要ログイン)
ファイルパターン | 結果 |
---|---|
sample | AC * 2 |
other | AC * 15 WA * 3 |
ソースコード
import sys import itertools # Set a higher recursion depth limit for potentially deep recursion in partition generation # Although max exponent K is relatively small (~20 for M < 10^6), the structure of recursion might need it. sys.setrecursionlimit(2000) # Define the modulus P = 10^9 + 7 MOD = 10**9 + 7 # Function for modular exponentiation (base^power % MOD) # Uses binary exponentiation (also known as exponentiation by squaring) for efficiency def fast_pow(base, power): result = 1 while power > 0: # If power is odd, multiply result with base if power % 2 == 1: result = (result * base) % MOD # Square the base and halve the power base = (base * base) % MOD power //= 2 return result # Function for modular inverse using Fermat's Little Theorem # Calculates a^(MOD-2) % MOD, which is the modular inverse of a modulo MOD (since MOD is prime) def inverse(a): # Check if a is 0 modulo MOD. If so, its inverse doesn't exist. # The size of an automorphism group |Aut(G)| is always >= 1 for |G| >= 1, so a should not be 0. if a == 0: raise ValueError("Modular inverse of 0 requested") return fast_pow(a, MOD - 2) # Precompute factorials modulo MOD up to the maximum possible value of M # M < 10^6, so we compute up to 10^6. MAX_M_limit = 10**6 fact = [1] * (MAX_M_limit + 1) for i in range(1, MAX_M_limit + 1): fact[i] = (fact[i - 1] * i) % MOD # Function to calculate the size of the automorphism group of an abelian p-group # G = Z_{p^{e_1}} x ... x Z_{p^{e_n}} # The input 'partition' is a list of exponents [e1, e2, ..., en] # The formula used requires the partition structure represented as: # G = (Z_{p^{lambda_1}})^{m_1} x ... x (Z_{p^{lambda_k}})^{m_k} where lambda_1 > lambda_2 > ... > lambda_k > 0 # This function handles converting the input partition list to this structure internally. def calculate_aut_p_group_size(p, partition): # Count occurrences of each exponent value in the input partition counts = {} for x in partition: if x > 0: # Partition parts must be positive integers counts[x] = counts.get(x, 0) + 1 # If the partition is empty (this corresponds to k=0 exponent, for M=1), # the group is the trivial group {e}, and |Aut({e})| = 1. if not counts: return 1 # Get the distinct exponents (lambda_i) and sort them in descending order distinct_exponents = sorted(counts.keys(), reverse=True) lambdas = distinct_exponents # Get the multiplicities (m_i) corresponding to the sorted distinct exponents ms = [counts[lam] for lam in lambdas] k = len(lambdas) # Number of distinct exponent types # The formula for |Aut(G)| has three main product terms. Calculate them modularly. # Term 1: Product related to GL(m_i, F_p) factors term1 = 1 for i in range(k): # Loop over distinct exponent types lambda_i m_i = ms[i] # Calculate p^{m_i} mod MOD p_pow_m_i = fast_pow(p, m_i) current_prod = 1 # Inner product runs from j=0 to m_i-1 for j in range(m_i): # Calculate p^j mod MOD p_pow_j = fast_pow(p, j) # Calculate (p^{m_i} - p^j) mod MOD. Add MOD before taking modulo to handle potential negative result. val = (p_pow_m_i - p_pow_j + MOD) % MOD current_prod = (current_prod * val) % MOD term1 = (term1 * current_prod) % MOD # Term 2: Product over pairs of distinct exponents (1 <= i < j <= k) term2 = 1 for i in range(k): for j in range(i + 1, k): # j > i lambda_j = lambdas[j] m_i = ms[i] m_j = ms[j] # Calculate (p^{lambda_j})^(m_i * m_j) mod MOD factor1 = fast_pow(p, lambda_j) factor1_pow = m_i * m_j # Exponent is relatively small, direct computation is fine term2_part1 = fast_pow(factor1, factor1_pow) # Calculate (p^{m_j})^(m_i * m_j) mod MOD factor2 = fast_pow(p, m_j) factor2_pow = m_i * m_j term2_part2 = fast_pow(factor2, factor2_pow) # Accumulate product terms modulo MOD term2 = (term2 * term2_part1) % MOD term2 = (term2 * term2_part2) % MOD # Term 3: Product related to powers p^{lambda_i - 1} term3 = 1 for i in range(k): # Loop over distinct exponent types lambda_i lambda_i = lambdas[i] m_i = ms[i] # Base is p^{lambda_i - 1} factor = fast_pow(p, lambda_i - 1) # Exponent is m_i^2 exponent = m_i * m_i # Calculate (p^{lambda_i - 1})^(m_i^2) mod MOD term3_part = fast_pow(factor, exponent) # Accumulate product term modulo MOD term3 = (term3 * term3_part) % MOD # Combine all three terms to get the final size of the automorphism group total_aut_size = (term1 * term2) % MOD total_aut_size = (total_aut_size * term3) % MOD return total_aut_size # Function for prime factorization of an integer n def prime_factorize(n): factors = {} d = 2 # Use trial division up to sqrt(n) while d * d <= n: while n % d == 0: factors[d] = factors.get(d, 0) + 1 n //= d d += 1 # If n is still greater than 1 after the loop, it must be a prime factor itself if n > 1: factors[n] = factors.get(n, 0) + 1 return factors # Function to generate integer partitions of k with memoization # Generates partitions as lists of integers in non-decreasing order, e.g., [1, 1, 2] for k=4. memo_partitions = {} def generate_partitions_memo(k): # Use k as the key for memoization state = k if state in memo_partitions: return memo_partitions[state] # Base case: Partition of 0 is represented by a list containing an empty list if k == 0: return [[]] partitions = [] # 'i' represents the smallest part in the partition being constructed for i in range(1, k + 1): # Recursively find partitions for the remaining sum k-i results_k_minus_i = generate_partitions_memo(k - i) for result in results_k_minus_i: # Ensure non-decreasing order: # The current part 'i' must be less than or equal to the smallest part in the 'result' list (if 'result' is not empty) if not result or result[0] >= i: # If the condition is met, prepend 'i' to the sub-partition 'result' partitions.append([i] + result) # Store the generated partitions in the memoization table and return them memo_partitions[state] = partitions return partitions # Main program logic # Read input M M = int(sys.stdin.readline()) # Handle the trivial case M=1. The only group is the trivial group on {0}. There is 1 such structure. if M == 1: print(1) sys.exit() # Exit after printing the result # Compute the prime factorization of M. Example: M=12 -> {2: 2, 3: 1} prime_factors = prime_factorize(M) # Get list of prime factors p_i primes = list(prime_factors.keys()) # Get list of corresponding exponents k_i exponents = [prime_factors[p] for p in primes] # Generate all partitions for each exponent k_i. Store these lists of partitions. partitions_for_exponents = [] for k_i in exponents: partitions_for_exponents.append(generate_partitions_memo(k_i)) # Calculate M! mod MOD using the precomputed table M_factorial = fact[M] # Initialize the total sum of terms M! / |Aut(G)| total_sum = 0 # An abelian group G of order M is isomorphic to a direct product of its Sylow p-subgroups G_p. # The structure of each G_p is determined by a partition of the exponent k_i of p in M. # We need to iterate through all combinations of partitions, one for each prime factor p_i. # `itertools.product` generates the Cartesian product of the lists of partitions. list_of_partition_lists = partitions_for_exponents combinations = list(itertools.product(*list_of_partition_lists)) # Each 'combo' is a tuple of partitions, e.g., (partition_for_p1, partition_for_p2, ...). # This tuple represents one isomorphism class of an abelian group G of order M. for combo in combinations: # Calculate the size of the automorphism group |Aut(G)| for this isomorphism class. # |Aut(G)| = product of |Aut(G_p)| for each Sylow p-subgroup G_p. current_aut_size = 1 for i in range(len(primes)): p = primes[i] # The prime factor partition = combo[i] # The partition defining the structure of G_p # Calculate |Aut(G_p)| using the dedicated function aut_p_group_size = calculate_aut_p_group_size(p, partition) # Multiply into the total |Aut(G)| size, modulo MOD current_aut_size = (current_aut_size * aut_p_group_size) % MOD # Calculate the term M! / |Aut(G)| modulo MOD. # This is equivalent to M! * (|Aut(G)|)^(-1) mod MOD. inv_aut_size = inverse(current_aut_size) term = (M_factorial * inv_aut_size) % MOD # Add this term to the total sum, modulo MOD total_sum = (total_sum + term) % MOD # Print the final total sum modulo MOD print(total_sum)