Problem p03108 (Python) — buggy submission, verdict: Runtime Error

class UnionFind:
    def __init__(self, nodes):
        self._parents = {k: k for k in nodes}
        self._ranks = {k: 0 for k in nodes}
        self._sizes = {k: 1 for k in nodes}

    def union(self, x, y):
        """
        x が属する木と y が属する木を併合
        :param x:
        :param y:
        :return:
        """
        x = self.find(x)
        y = self.find(y)
        if x != y:
            # rank が小さい方が下
            if self._ranks[x] > self._ranks[y]:
                self._parents[y] = x
                self._sizes[x] += self._sizes[y]
            else:
                self._parents[x] = y
                self._sizes[y] += self._sizes[x]
                if self._ranks[x] == self._ranks[y]:
                    self._ranks[y] += 1

    def find(self, x):
        """
        x が属する木の root
        :param x:
        :return:
        """
        if self._parents[x] == x:
            return x
        self._parents[x] = self.find(self._parents[x])
        return self._parents[x]

    def size(self, x):
        """
        x が属する木のノード数
        :param x:
        :return:
        """
        return self._sizes[self.find(x)]


n, m = map(int, input().split())

bridges = []
for _ in range(m):
    bridges.append(list(map(int, input().split())))


def c2(n):
    # n から2個選ぶ組み合わせ nC2
    return int(n * (n - 1) / 2)


bridges.reverse()
uf = UnionFind(nodes=[i + 1 for i in range(n)])
# 便利さ; つながってる島の組み合わせの数
useful = 0
useful_history = []

for bridge in bridges:
    useful_history.append(useful)

    if uf.find(bridge[0]) != uf.find(bridge[1]):
        # 新しくつながったので便利さを更新
        size0 = uf.size(bridge[0])
        size1 = uf.size(bridge[1])
        useful += c2(size0 + size1) - c2(size0) - c2(size1)
        uf.union(bridge[0], bridge[1])

for u in reversed(useful_history):
    print(useful - u)


Error: 0

Fixed submission:

class UnionFind:
    def __init__(self, nodes):
        self._parents = {k: k for k in nodes}
        self._ranks = {k: 0 for k in nodes}
        self._sizes = {k: 1 for k in nodes}

    def union(self, x, y):
        x_root = self.find(x)
        y_root = self.find(y)
        if x_root != y_root:
            if self._ranks[x_root] < self._ranks[y_root]:
                self._parents[y_root] = x_root
                self._ranks[x_root] += 1
                self._sizes[x_root] += self._sizes[y_root]
            else:
                self._parents[x_root] = y_root
                self._ranks[y_root] += 1
                self._sizes[y_root] += self._sizes[x_root]

    def find(self, x):
        if self._parents[x] == x:
            return x
        self._parents[x] = self.find(self._parents[x])
        return self._parents[x]

    def size(self, x):
        return self._sizes[self.find(x)]


n, m = map(int, input().split())

bridges = []
for _ in range(m):
    bridges.append(list(map(int, input().split())))


def c2(n):
    # n から2個選ぶ組み合わせ nC2
    return int(n * (n - 1) / 2)


bridges.reverse()
uf = UnionFind(nodes=[i + 1 for i in range(n)])
# 便利さ; つながってる島の組み合わせの数
useful = 0
useful_history = []

for bridge in bridges:
    useful_history.append(useful)

    if uf.find(bridge[0]) != uf.find(bridge[1]):
        # 新しくつながったので便利さを更新
        size0 = uf.size(bridge[0])
        size1 = uf.size(bridge[1])
        useful += c2(size0 + size1) - c2(size0) - c2(size1)
        uf.union(bridge[0], bridge[1])

for u in reversed(useful_history):
    print(useful - u)