結果
| 問題 |
No.3200 Sinking Islands
|
| コンテスト | |
| ユーザー |
|
| 提出日時 | 2025-07-03 16:47:22 |
| 言語 | PyPy3 (7.3.15) |
| 結果 |
AC
|
| 実行時間 | 527 ms / 2,000 ms |
| コード長 | 3,740 bytes |
| コンパイル時間 | 357 ms |
| コンパイル使用メモリ | 82,152 KB |
| 実行使用メモリ | 106,108 KB |
| 最終ジャッジ日時 | 2025-07-03 16:52:04 |
| 合計ジャッジ時間 | 11,098 ms |
|
ジャッジサーバーID (参考情報) |
judge2 / judge3 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| sample | AC * 2 |
| other | AC * 20 |
ソースコード
# f.py
import sys
class DSU:
"""
DSU (Disjoint Set Union) または Union-Find データ構造。
経路圧縮 (Path Compression) とサイズによる結合 (Union by Size) を実装しています。
"""
def __init__(self, n: int):
"""
Args:
n (int): 要素の数。0からnまでのn+1個の要素を初期化します。
"""
# 各要素の親を格納するリスト。最初は自身が親。
self.parent = list(range(n + 1))
# 各集合のサイズを格納するリスト
self.sz = [1] * (n + 1)
def find(self, i: int) -> int:
"""
要素iが属する集合の代表元(ルート)を探します。
経路圧縮を行い、計算量を削減します。
Args:
i (int): 探す対象の要素。
Returns:
int: 要素iが属する集合の代表元。
"""
if self.parent[i] == i:
return i
self.parent[i] = self.find(self.parent[i])
return self.parent[i]
def unite(self, i: int, j: int):
"""
要素iが属する集合と要素jが属する集合を併合します。
サイズが小さい方の木を大きい方の木に結合します(Union by Size)。
Args:
i (int): 併合する要素1。
j (int): 併合する要素2。
"""
root_i = self.find(i)
root_j = self.find(j)
if root_i != root_j:
if self.sz[root_i] < self.sz[root_j]:
root_i, root_j = root_j, root_i
self.parent[root_j] = root_i
self.sz[root_i] += self.sz[root_j]
def f_solver():
"""
メインの処理。
"""
# 高速入出力
input = sys.stdin.readline
N, M = map(int, input().split())
edges = [tuple(map(int, input().split())) for _ in range(M)]
Q = int(input())
# クエリで指定された辺のインデックスを0-indexedで読み込む
query_edges = [int(input()) - 1 for _ in range(Q)]
# クエリで削除される辺をマーク
is_removed = [False] * M
for edge_idx in query_edges:
is_removed[edge_idx] = True
# DSUをN個の頂点で初期化
dsu = DSU(N)
# クエリで削除されない辺を使って、初期の連結成分を構築
for i in range(M):
if not is_removed[i]:
u, v = edges[i]
dsu.unite(u, v)
# 初期の非連結ペアの数を計算
current_disconnected_pairs = 0
visited_root = [False] * (N + 1)
connected_nodes = 0
for i in range(1, N + 1):
root = dsu.find(i)
if not visited_root[root]:
size = dsu.sz[root]
# 現在の連結成分のノードと、まだ訪れていないノードとのペア数を加算
current_disconnected_pairs += size * (N - connected_nodes - size)
connected_nodes += size
visited_root[root] = True
# クエリを逆順に処理(削除された辺を元に戻していく)
ans = [0] * Q
for i in range(Q - 1, -1, -1):
ans[i] = current_disconnected_pairs
edge_idx = query_edges[i]
u, v = edges[edge_idx]
root_u = dsu.find(u)
root_v = dsu.find(v)
# 辺を追加することで異なる連結成分が1つになる場合、
# 非連結ペアの数は (成分uのサイズ * 成分vのサイズ) だけ減少する
if root_u != root_v:
current_disconnected_pairs -= dsu.sz[root_u] * dsu.sz[root_v]
dsu.unite(u, v)
# 答えをまとめて出力
for val in ans:
print(val)
if __name__ == '__main__':
f_solver()