結果

問題 No.1451 集団登校
ユーザー qwewe
提出日時 2025-05-14 13:16:37
言語 PyPy3
(7.3.15)
結果
AC  
実行時間 291 ms / 2,000 ms
コード長 7,096 bytes
コンパイル時間 186 ms
コンパイル使用メモリ 82,636 KB
実行使用メモリ 93,484 KB
最終ジャッジ日時 2025-05-14 13:17:58
合計ジャッジ時間 5,711 ms
ジャッジサーバーID
(参考情報)
judge2 / judge5
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
sample AC * 3
other AC * 27
権限があれば一括ダウンロードができます

ソースコード

diff #

import sys

# Setting a higher recursion depth limit might be necessary for deep DSU paths
# in worst-case scenarios, although iterative find is used which avoids stack depth issues for find().
# Setting it defensively. 2*10^5 + 10 is a safe upper bound N + M + buffer.
try:
    # Increase the recursion depth limit for potentially deep call stacks
    # (though unlikely with iterative find).
    sys.setrecursionlimit(200010) 
except (ValueError, OverflowError):
    # If the system restricts setting a high limit (e.g., OS limits),
    # proceed with the default limit. The iterative find function helps mitigate this risk.
    pass 

# Define the modulus for calculations
MOD = 10**9 + 7

# Function for fast modular exponentiation (base^power % MOD)
# Used for calculating modular inverse efficiently.
def fast_pow(base, power):
    """
    Computes (base^power) % MOD using binary exponentiation.
    """
    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 to calculate modular multiplicative inverse using Fermat's Little Theorem
# Calculates a^(MOD-2) % MOD, which is the inverse of a modulo MOD (if MOD is prime).
def inverse(a):
    """
    Computes the modular multiplicative inverse of a modulo MOD.
    Assumes MOD is a prime number and a is not divisible by MOD.
    """
    return fast_pow(a, MOD - 2)

# Precompute the modular inverse of 2, as it's needed frequently when merging equal-sized groups.
inv2 = inverse(2)

# Global variables for Disjoint Set Union (DSU) state. Using globals simplifies function calls.
parent = []  # parent[i] stores the parent of node i
size = []    # size[i] stores the size of the set rooted at i (only valid for roots)
prob = []    # prob[i] stores the probability that student i is the leader of their current group
members = [] # members[i] is a list storing student IDs in the group rooted at i (only valid for roots)

# Iterative Find operation with Path Compression
# Finds the representative (root) of the set containing element i and compresses the path.
def find(i):
    """
    Finds the root of the set containing element i using iterative path compression.
    Returns the root of the set.
    """
    global parent # Indicate usage of global parent list
    root = i
    path = [] # Stores nodes encountered on the path to the root
    
    # Traverse up the tree to find the root
    while parent[root] != root:
        path.append(root)
        root = parent[root]
    
    # Apply path compression: make all nodes on the path point directly to the root
    # This optimizes future find operations for these nodes.
    for node in path:
        parent[node] = root
    return root

# Union operation logic: merges the sets containing elements a and b
def unite(a, b):
    """
    Merges the groups containing students a and b according to the problem rules.
    Updates probabilities based on group sizes. Uses union-by-size heuristic.
    """
    global parent, size, prob, members, inv2, MOD # Indicate modification of global state variables

    # Find the roots of the sets containing a and b
    Ra = find(a)
    Rb = find(b)

    # If a and b are already in the same set, no action is needed
    if Ra == Rb:
        return

    # Union by size heuristic: Always merge the smaller set into the larger set.
    # This keeps the DSU trees relatively balanced and ensures efficient operations.
    if size[Ra] > size[Rb]:
        # Swap Ra and Rb so that Ra always represents the root of the smaller (or equal size) set
        Ra, Rb = Rb, Ra 
    
    # Retrieve the sizes of the groups AFTER potentially swapping Ra and Rb
    Sa = size[Ra]
    Sb = size[Rb]

    # Case 1: The group rooted at Ra is strictly smaller than the group rooted at Rb
    if Sa < Sb:
        # Merge Ra's group into Rb's group.
        # According to the rules, members of the smaller group (Ra's group) are eliminated
        # as potential leaders. Their probability becomes 0.
        for k in members[Ra]:
            prob[k] = 0
        
        # Transfer the list of members from Ra's group to Rb's group.
        # list.extend is generally efficient for this.
        members[Rb].extend(members[Ra])
        # Clear the member list for Ra. Reassigning to an empty list is efficient in Python.
        members[Ra] = [] 
        
        # Update the DSU structure: set Ra's parent to Rb and update Rb's size.
        parent[Ra] = Rb
        size[Rb] += Sa
        # The size entry for Ra is now irrelevant as it's no longer a root.
        
    # Case 2: The groups have equal size
    else: # Sa == Sb
        # Merge Ra's group into Rb's group (arbitrary choice, could be Rb into Ra).
        # When equal-sized groups merge, the probabilities of members in BOTH groups are halved.
        
        # Halve probabilities for members originally in Ra's group
        for k in members[Ra]:
            prob[k] = (prob[k] * inv2) % MOD
        
        # Halve probabilities for members originally in Rb's group
        for k in members[Rb]:
            prob[k] = (prob[k] * inv2) % MOD

        # Transfer members from Ra's list to Rb's list
        members[Rb].extend(members[Ra])
        # Clear Ra's list
        members[Ra] = []

        # Update the DSU structure: set Ra's parent to Rb and update Rb's size.
        parent[Ra] = Rb
        size[Rb] += Sa
        # The size entry for Ra is now irrelevant.

# Main function to orchestrate the process
def solve():
    """
    Reads input, initializes data structures, processes merges, and prints results.
    """
    global parent, size, prob, members # Ensure access to global variables

    # Read number of students (N) and number of merge operations (M)
    N, M = map(int, sys.stdin.readline().split())

    # Initialize DSU structure, probabilities, and member lists
    # Using 1-based indexing for students (1 to N), so arrays are size N+1. Index 0 is unused.
    parent = [i for i in range(N + 1)]
    size = [1] * (N + 1)
    prob = [0] * (N + 1) # Initialize probability array; will set prob[1..N] to 1
    members = [[] for _ in range(N + 1)] # Initialize list of lists for members

    # Set up initial state: each student is in their own group of size 1,
    # is the leader of that group with probability 1.
    for i in range(1, N + 1):
        prob[i] = 1
        members[i].append(i) # Student i is initially the only member of the group rooted at i

    # Process all M merge operations sequentially
    for _ in range(M):
        # Read the pair of students (a, b) involved in the current merge operation
        a, b = map(int, sys.stdin.readline().split())
        # Perform the unite operation on the groups containing a and b
        unite(a, b)

    # After processing all merges, output the final probability for each student
    for i in range(1, N + 1):
        print(prob[i])

# Execute the main solver function
solve()
0