結果

問題 No.1326 ふたりのDominator
ユーザー gew1fw
提出日時 2025-06-12 20:22:08
言語 PyPy3
(7.3.15)
結果
RE  
実行時間 -
コード長 7,731 bytes
コンパイル時間 394 ms
コンパイル使用メモリ 81,712 KB
実行使用メモリ 138,664 KB
最終ジャッジ日時 2025-06-12 20:23:00
合計ジャッジ時間 6,177 ms
ジャッジサーバーID
(参考情報)
judge2 / judge1
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
sample AC * 1
other AC * 9 RE * 2 TLE * 1 -- * 12
権限があれば一括ダウンロードができます

ソースコード

diff #

import sys
from sys import stdin
from collections import defaultdict, deque

sys.setrecursionlimit(1 << 25)

def main():
    input = sys.stdin.read().split()
    ptr = 0
    N, M = int(input[ptr]), int(input[ptr+1])
    ptr +=2
    adj = [[] for _ in range(N+1)]
    for _ in range(M):
        u = int(input[ptr])
        v = int(input[ptr+1])
        adj[u].append(v)
        adj[v].append(u)
        ptr +=2

    # Step 1: Find articulation points and biconnected components using Tarjan's algorithm
    disc = [0]*(N+1)
    low = [0]*(N+1)
    time_counter = [1]
    ap = [False]*(N+1)
    parent = [0]*(N+1)
    stack = []
    bcc = []
    block_edges_list = []

    def tarjan(u):
        disc[u] = low[u] = time_counter[0]
        time_counter[0] +=1
        children = 0
        for v in adj[u]:
            if disc[v] == 0:
                parent[v] = u
                stack.append((u, v))
                children +=1
                tarjan(v)
                low[u] = min(low[u], low[v])
                if (parent[u] == 0 and children >=2) or (parent[u] !=0 and low[v] >= disc[u]):
                    ap[u] = True
                    current_bcc = []
                    while True:
                        edge = stack.pop()
                        current_bcc.append(edge)
                        if edge == (u, v):
                            break
                    block_edges_list.append(current_bcc)
            elif v != parent[u] and disc[v] < disc[u]:
                stack.append((u, v))
                low[u] = min(low[u], disc[v])
        return

    for u in range(1, N+1):
        if disc[u] == 0:
            tarjan(u)
            if stack:
                current_bcc = []
                while stack:
                    edge = stack.pop()
                    current_bcc.append(edge)
                block_edges_list.append(current_bcc)

    # Collect all articulation points
    articulation_points = set(u for u in range(1, N+1) if ap[u])

    # Step 2: Build BC tree
    # Assign each block and articulation point to a node in BC tree
    # Create mappings
    block_nodes = []
    ap_nodes = []
    block_id = 0
    node_to_bc = {}
    bc_tree_adj = defaultdict(list)

    # Process each block
    for bid, edges in enumerate(block_edges_list):
        nodes = set()
        for u, v in edges:
            nodes.add(u)
            nodes.add(v)
        block_node = ('block', bid)
        block_nodes.append(block_node)
        # Connect to articulation points in this block
        aps_in_block = set()
        for node in nodes:
            if ap[node]:
                aps_in_block.add(node)
        for ap_node in aps_in_block:
            ap_bc_node = ('ap', ap_node)
            bc_tree_adj[block_node].append(ap_bc_node)
            bc_tree_adj[ap_bc_node].append(block_node)

    # Process articulation points and their blocks
    for u in articulation_points:
        ap_bc_node = ('ap', u)
        # Find all blocks that include this articulation point
        for bid, edges in enumerate(block_edges_list):
            nodes = set()
            for x, y in edges:
                nodes.add(x)
                nodes.add(y)
            if u in nodes:
                block_node = ('block', bid)
                if block_node not in bc_tree_adj[ap_bc_node]:
                    bc_tree_adj[ap_bc_node].append(block_node)
                    bc_tree_adj[block_node].append(ap_bc_node)

    # Step 3: Assign each original node to BC node
    node_to_bc = {}
    # For non-articulation points, find their block
    non_ap_block = {}
    for bid, edges in enumerate(block_edges_list):
        nodes = set()
        for u, v in edges:
            nodes.add(u)
            nodes.add(v)
        for node in nodes:
            if not ap[node]:
                if node not in non_ap_block:
                    non_ap_block[node] = ('block', bid)

    # Step 4: Preprocess BC tree for LCA and art_count
    # Convert BC tree nodes to unique identifiers
    bc_nodes = set()
    for node in bc_tree_adj:
        bc_nodes.add(node)
    bc_node_list = list(bc_nodes)
    node_to_idx = {node: i for i, node in enumerate(bc_node_list)}
    idx_to_node = {i: node for i, node in enumerate(bc_node_list)}
    total_nodes = len(bc_node_list)
    tree = [[] for _ in range(total_nodes)]
    for node in bc_tree_adj:
        idx = node_to_idx[node]
        for neighbor in bc_tree_adj[node]:
            neighbor_idx = node_to_idx[neighbor]
            tree[idx].append(neighbor_idx)

    # BFS to setup parent, depth, and art_count
    LOG = 20
    parent_lca = [[-1]*total_nodes for _ in range(LOG)]
    depth = [0]*total_nodes
    art_count = [0]*total_nodes

    # Choose root as the first node (arbitrary)
    root = 0
    q = deque()
    q.append(root)
    parent_lca[0][root] = -1
    visited = [False]*total_nodes
    visited[root] = True
    while q:
        u = q.popleft()
        for v in tree[u]:
            if not visited[v]:
                visited[v] = True
                parent_lca[0][v] = u
                depth[v] = depth[u] + 1
                # art_count: if current node is articulation point, add 1
                current_node = idx_to_node[v]
                if current_node[0] == 'ap':
                    art_count[v] = art_count[u] + 1
                else:
                    art_count[v] = art_count[u]
                q.append(v)

    # Fill parent_lca for binary lifting
    for k in range(1, LOG):
        for v in range(total_nodes):
            if parent_lca[k-1][v] != -1:
                parent_lca[k][v] = parent_lca[k-1][parent_lca[k-1][v]]

    # LCA function
    def lca(u, v):
        if depth[u] < depth[v]:
            u, v = v, u
        # Bring u to the same depth as v
        for k in range(LOG-1, -1, -1):
            if depth[u] - (1 << k) >= depth[v]:
                u = parent_lca[k][u]
        if u == v:
            return u
        for k in range(LOG-1, -1, -1):
            if parent_lca[k][u] != -1 and parent_lca[k][u] != parent_lca[k][v]:
                u = parent_lca[k][u]
                v = parent_lca[k][v]
        return parent_lca[0][u]

    # Function to get art count between two nodes
    def get_art_count(u_idx, v_idx):
        ancestor_idx = lca(u_idx, v_idx)
        count = art_count[u_idx] + art_count[v_idx] - 2 * art_count[ancestor_idx]
        ancestor_node = idx_to_node[ancestor_idx]
        if ancestor_node[0] == 'ap':
            count +=1
        return count

    # Process queries
    Q = int(input[ptr])
    ptr +=1
    for _ in range(Q):
        x = int(input[ptr])
        y = int(input[ptr+1])
        ptr +=2
        if x == y:
            print(0)
            continue
        # Get Bx and By
        if x in articulation_points:
            Bx = ('ap', x)
        else:
            if x not in non_ap_block:
                print(0)
                continue
            Bx = non_ap_block[x]
        if y in articulation_points:
            By = ('ap', y)
        else:
            if y not in non_ap_block:
                print(0)
                continue
            By = non_ap_block[y]
        if Bx == By:
            print(0)
            continue
        # Convert Bx and By to indices
        if Bx not in node_to_idx or By not in node_to_idx:
            print(0)
            continue
        u = node_to_idx[Bx]
        v = node_to_idx[By]
        # Get art count
        cnt = get_art_count(u, v)
        # Subtract x and y if they are articulation points
        subtract = 0
        if x in articulation_points:
            subtract +=1
        if y in articulation_points:
            subtract +=1
        ans = cnt - subtract
        print(max(0, ans))

if __name__ == "__main__":
    main()
0