Buggy python submission (verdict: Time Limit Exceeded):
class UnionFindTree:
    '''
    Union Find木の実装
    ランクは無視する
    '''
    
    def __init__(self, n):
        '''ノード数nで初期化'''
        # nodes[i] == i すなわち各ノードは自身が根
        self.nodes = list(range(n))

    def isSameTree(self, idxA, idxB):
        return self.root(idxA) == self.root(idxB)
    
    def union(self, idxA, idxB):
        '''ノードAを含む木とノードBを含む木を結合'''
        a = self.root(idxA)
        b = self.root(idxB)

        # b番目のノードの根をaにする
        if a != b:
            self.nodes[b] = a
    
    def root(self, idx):
        '''ノードの根番号と深さを探索し、経路圧縮を行う'''
        
        # 探索
        root_idx = idx
        while root_idx != self.nodes[root_idx]:
            root_idx = self.nodes[root_idx]

        # 経路圧縮
        _idx = idx
        while root_idx != _idx:
            _idx, self.nodes[_idx] = self.nodes[_idx], root_idx

        return _idx

###========================

N, M = map(int, input().split())
A = []
B = []
for i in range(M):
    a, b = map(int, input().split())
    A.append(a)
    B.append(b)


tree = UnionFindTree(N)
def hubensa(tree):
    n = len(tree.nodes)
    cnt = 0
    for i in range(n):
        for j in range(i+1, n):
            if not tree.isSameTree(i, j):
                cnt += 1
    return cnt

hubensa_list = [None] * M
last_changed = True
for i in range(M-1, -1, -1):
    if tree.isSameTree(A[i]-1, B[i]-1):
        if last_changed:
            last_changed = False
            hubensa_list[i] = hubensa(tree)
        else:
            hubensa_list[i] = hubensa_list[i+1]
            
    else:
        hubensa_list[i] = hubensa(tree)
        last_changed = True
    tree.union(A[i]-1, B[i]-1)

for x in hubensa_list:
    print(x)

Fixed submission:
class UnionFindTree:
    '''
    Union Find木の実装
    ランクは無視する
    '''
    
    def __init__(self, n):
        '''ノード数nで初期化'''
        # nodes[i] < 0 すなわち各ノードは自身が根
        # 根の値はその木の要素数の符号反転したもの
        self.nodes = [-1] * n

    def is_root(self, idx):
        '''根ならTrue、根以外ならFalse'''
        return self.nodes[idx] < 0
        
    def is_same_group(self, idxA, idxB):
        return self.root(idxA) == self.root(idxB)
    
    def size(self, idx):
        '''ノードを含むグループのノード数を返す'''
        return -self.nodes[self.root(idx)]

    def union(self, idxA, idxB):
        '''ノードAを含む木とノードBを含む木を結合'''
        a = self.root(idxA)
        b = self.root(idxB)

        size_a = self.size(idxA)
        size_b  =self.size(idxB)

        # b番目のノードの根をaにする
        if a != b:
            self.nodes[b] = a
        self.nodes[a] -= size_b
    
    def root(self, idx):
        '''ノードの根番号を返す。途中で経路圧縮を行う'''
        
        # 探索
        _idx = idx
        while not self.is_root(_idx):
            _idx = self.nodes[_idx]
        root_idx = _idx

        # 経路圧縮
        _idx = idx
        while root_idx != _idx:
            tmp = _idx
            _idx = self.nodes[_idx]
            self.nodes[tmp] = root_idx

        return root_idx
###========================

N, M = map(int, input().split())
A = []
B = []
for i in range(M):
    a, b = map(int, input().split())
    A.append(a)
    B.append(b)


tree = UnionFindTree(N)

hubensa_list = [None] * M
hubensa_list[M-1] = N * (N-1) // 2

for i in range(M-2, -1, -1):
    if tree.is_same_group(A[i+1]-1, B[i+1]-1):
        hubensa_list[i] = hubensa_list[i+1]    
    else:
        sa = tree.size(A[i+1]-1)
        sb = tree.size(B[i+1]-1)
        hubensa_list[i] = hubensa_list[i+1] - sa * sb
        tree.union(A[i+1]-1, B[i+1]-1)

for x in hubensa_list:
    print(x)

Buggy python submission (verdict: Time Limit Exceeded):
class UnionFindTree:
    '''
    Union Find木の実装
    ランクは無視する
    '''
    
    def __init__(self, n):
        '''ノード数nで初期化'''
        # nodes[i] == i すなわち各ノードは自身が根
        self.nodes = list(range(n))

    def isSameTree(self, idxA, idxB):
        return self.root(idxA) == self.root(idxB)
    
    def union(self, idxA, idxB):
        '''ノードAを含む木とノードBを含む木を結合'''
        a = self.root(idxA)
        b = self.root(idxB)

        # b番目のノードの根をaにする
        if a != b:
            self.nodes[b] = a
    
    def root(self, idx):
        '''ノードの根番号と深さを探索し、経路圧縮を行う'''
        
        # 探索
        root_idx = idx
        while root_idx != self.nodes[root_idx]:
            root_idx = self.nodes[root_idx]

        # 経路圧縮
        _idx = idx
        while root_idx != _idx:
            _idx, self.nodes[_idx] = self.nodes[_idx], root_idx

        return _idx

###========================

N, M = map(int, input().split())
A = []
B = []
for i in range(M):
    a, b = map(int, input().split())
    A.append(a)
    B.append(b)


tree = UnionFindTree(N)
def hubensa(tree):
    n = len(tree.nodes)
    cnt = 0
    for i in range(n):
        for j in range(i+1, n):
            if not tree.isSameTree(i, j):
                cnt += 1
    return cnt

hubensa_list = [None] * M
for i in range(M-1, -1, -1):
    hubensa_list[i] = hubensa(tree)
    tree.union(A[i]-1, B[i]-1)

for x in hubensa_list:
    print(x)

Fixed submission:
class UnionFindTree:
    '''
    Union Find木の実装
    ランクは無視する
    '''
    
    def __init__(self, n):
        '''ノード数nで初期化'''
        # nodes[i] < 0 すなわち各ノードは自身が根
        # 根の値はその木の要素数の符号反転したもの
        self.nodes = [-1] * n

    def is_root(self, idx):
        '''根ならTrue、根以外ならFalse'''
        return self.nodes[idx] < 0
        
    def is_same_group(self, idxA, idxB):
        return self.root(idxA) == self.root(idxB)
    
    def size(self, idx):
        '''ノードを含むグループのノード数を返す'''
        return -self.nodes[self.root(idx)]

    def union(self, idxA, idxB):
        '''ノードAを含む木とノードBを含む木を結合'''
        a = self.root(idxA)
        b = self.root(idxB)

        size_a = self.size(idxA)
        size_b  =self.size(idxB)

        # b番目のノードの根をaにする
        if a != b:
            self.nodes[b] = a
        self.nodes[a] -= size_b
    
    def root(self, idx):
        '''ノードの根番号を返す。途中で経路圧縮を行う'''
        
        # 探索
        _idx = idx
        while not self.is_root(_idx):
            _idx = self.nodes[_idx]
        root_idx = _idx

        # 経路圧縮
        _idx = idx
        while root_idx != _idx:
            tmp = _idx
            _idx = self.nodes[_idx]
            self.nodes[tmp] = root_idx

        return root_idx
###========================

N, M = map(int, input().split())
A = []
B = []
for i in range(M):
    a, b = map(int, input().split())
    A.append(a)
    B.append(b)


tree = UnionFindTree(N)

hubensa_list = [None] * M
hubensa_list[M-1] = N * (N-1) // 2

for i in range(M-2, -1, -1):
    if tree.is_same_group(A[i+1]-1, B[i+1]-1):
        hubensa_list[i] = hubensa_list[i+1]    
    else:
        sa = tree.size(A[i+1]-1)
        sb = tree.size(B[i+1]-1)
        hubensa_list[i] = hubensa_list[i+1] - sa * sb
        tree.union(A[i+1]-1, B[i+1]-1)

for x in hubensa_list:
    print(x)