結果

問題 No.1326 ふたりのDominator
ユーザー lam6er
提出日時 2025-04-15 23:38:24
言語 PyPy3
(7.3.15)
結果
RE  
実行時間 -
コード長 6,771 bytes
コンパイル時間 149 ms
コンパイル使用メモリ 82,232 KB
実行使用メモリ 313,296 KB
最終ジャッジ日時 2025-04-15 23:39:48
合計ジャッジ時間 13,277 ms
ジャッジサーバーID
(参考情報)
judge1 / judge5
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
sample RE * 1
other AC * 3 RE * 21
権限があれば一括ダウンロードができます

ソースコード

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
    graph = [[] for _ in range(N+1)]
    for _ in range(M):
        u = int(input[ptr])
        v = int(input[ptr+1])
        graph[u].append(v)
        graph[v].append(u)
        ptr +=2
    Q = int(input[ptr])
    ptr +=1
    queries = []
    for _ in range(Q):
        x = int(input[ptr])
        y = int(input[ptr+1])
        queries.append((x, y))
        ptr +=2

    # Biconnected components decomposition
    ord_ = [-1] * (N + 1)
    low = [-1] * (N + 1)
    is_art = [False] * (N + 1)
    stack = []
    blocks = []
    index = 0

    def dfs(v, parent):
        nonlocal index
        ord_[v] = index
        low[v] = index
        index +=1
        children = 0
        for to in graph[v]:
            if to == parent:
                continue
            if ord_[to] == -1:
                stack.append((v, to))
                children +=1
                dfs(to, v)
                low[v] = min(low[v], low[to])
                if (parent == -1 and children >=2) or (parent != -1 and low[to] >= ord_[v]):
                    is_art[v] = True
                    block = []
                    while True:
                        e = stack.pop()
                        block.append(e)
                        if e == (v, to):
                            break
                    blocks.append(block)
            elif ord_[to] < ord_[v]:
                stack.append((v, to))
                low[v] = min(low[v], ord_[to])
        if parent == -1 and children ==0:
            blocks.append([(v, to) for to in graph[v]])

    for v in range(1, N+1):
        if ord_[v] == -1:
            dfs(v, -1)
            if stack:
                block = []
                while stack:
                    e = stack.pop()
                    block.append(e)
                if block:
                    blocks.append(block)

    art_nodes = set(v for v in range(1, N+1) if is_art[v])

    block_edges = defaultdict(set)
    vertex_to_block = defaultdict(list)
    block_id = 0
    block_to_vertices = []
    for blk in blocks:
        vertices = set()
        for u, v in blk:
            vertices.add(u)
            vertices.add(v)
        block_to_vertices.append(vertices)
        for v in vertices:
            vertex_to_block[v].append(block_id)
        block_id +=1

    block_tree = defaultdict(list)
    block_id_to_node = {}
    node_counter = 0
    for bid in range(len(blocks)):
        block_node = - (bid +1)
        block_id_to_node[bid] = block_node
        vertices = block_to_vertices[bid]
        arts_in_block = []
        for v in vertices:
            if is_art[v]:
                arts_in_block.append(v)
        for art in arts_in_block:
            block_tree[block_node].append(art)
            block_tree[art].append(block_node)

    belongs_to = [0]*(N+1)
    for v in range(1, N+1):
        if is_art[v]:
            belongs_to[v] = v
        else:
            for bid in vertex_to_block[v]:
                belongs_to[v] = block_id_to_node[bid]
                break

    all_nodes = set()
    for nodes in block_tree:
        all_nodes.add(nodes)
    for nodes in block_tree.values():
        all_nodes.update(nodes)
    all_nodes = list(all_nodes)
    node_to_idx = {node: i for i, node in enumerate(all_nodes)}
    idx_to_node = {i: node for i, node in enumerate(all_nodes)}
    num_nodes = len(all_nodes)

    parent = [[-1]*20 for _ in range(num_nodes)]
    depth = [0]*num_nodes
    art_count = [0]*num_nodes
    visited = [False]*num_nodes

    def is_art_node(node):
        return node > 0

    for root in all_nodes:
        if not visited[node_to_idx[root]]:
            q = deque()
            q.append((root, -1, 0, 0))
            while q:
                node, p_node, d, cnt = q.popleft()
                idx = node_to_idx[node]
                if visited[idx]:
                    continue
                visited[idx] = True
                depth[idx] = d
                parent[idx][0] = node_to_idx[p_node] if p_node != -1 else -1
                new_cnt = cnt + (1 if is_art_node(node) else 0)
                art_count[idx] = new_cnt
                for neighbor in block_tree[node]:
                    n_idx = node_to_idx[neighbor]
                    if not visited[n_idx]:
                        q.append((neighbor, node, d+1, new_cnt))

    for k in range(1, 20):
        for i in range(num_nodes):
            if parent[i][k-1] != -1:
                parent[i][k] = parent[parent[i][k-1]][k-1]
            else:
                parent[i][k] = -1

    def lca(u_idx, v_idx):
        if depth[u_idx] < depth[v_idx]:
            u_idx, v_idx = v_idx, u_idx
        for k in range(19, -1, -1):
            if parent[u_idx][k] != -1 and depth[parent[u_idx][k]] >= depth[v_idx]:
                u_idx = parent[u_idx][k]
        if u_idx == v_idx:
            return u_idx
        for k in range(19, -1, -1):
            if parent[u_idx][k] != parent[v_idx][k]:
                u_idx = parent[u_idx][k]
                v_idx = parent[v_idx][k]
        return parent[u_idx][0]

    def get_art_num(u_node, v_node):
        u_idx = node_to_idx[u_node]
        v_idx = node_to_idx[v_node]
        l = lca(u_idx, v_idx)
        total = art_count[u_idx] + art_count[v_idx] - 2 * art_count[l]
        if is_art_node(idx_to_node[l]):
            total +=1
        return total

    def is_ancestor(a_idx, b_idx):
        return lca(b_idx, a_idx) == a_idx

    def is_on_path(u_node, v_node, a_node):
        a_idx = node_to_idx[a_node]
        u_idx = node_to_idx[u_node]
        v_idx = node_to_idx[v_node]
        luv_idx = lca(u_idx, v_idx)
        lau_idx = lca(u_idx, a_idx)
        lav_idx = lca(v_idx, a_idx)
        if lau_idx != a_idx and lav_idx != a_idx:
            return False
        if lau_idx == a_idx:
            return is_ancestor(luv_idx, a_idx)
        else:
            return is_ancestor(luv_idx, a_idx)

    for x, y in queries:
        if x == y:
            print(0)
            continue
        u = belongs_to[x]
        v_node = belongs_to[y]
        if u == v_node:
            print(0)
            continue
        total = get_art_num(u, v_node)
        if is_art[x]:
            a_node = x
            a_idx = node_to_idx[a_node]
            u_idx = node_to_idx[u]
            v_idx = node_to_idx[v_node]
            if is_on_path(u, v_node, a_node):
                total -=1
        if is_art[y]:
            a_node = y
            if is_on_path(u, v_node, a_node):
                total -=1
        print(max(0, total))

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