import sys
sys.setrecursionlimit(10**7)
def LI(): return [int(x) for x in sys.stdin.readline().split()]
def LI_(): return [int(x) - 1 for x in sys.stdin.readline().split()]
def LF(): return [float(x) for x in sys.stdin.readline().split()]
def LS(): return sys.stdin.readline().split()
def II(): return int(sys.stdin.readline())
def SI(): return sys.stdin.readline().strip()
def LILI(n): return [LI() for _ in range(n)]
INF = 10 ** 18
MOD = 10 ** 9 + 7


'''
独立なグラフがいくつかできていく。
求めるもの：グラフ1にある頂点と2にある頂点の combinaton, 1と3のcombination,……2と3のcombination…… の総和を求めるのを繰り返す。
union-findで行けそうだがN, Mどちらも10**5なので間に合わなさそう。

最終的にはすべて独立となり、答えはnC2となる。
ということで nC2 からはじめてどんどん union していけばいいのでは。
union する2つのグループの頂点数の積を引いていけば良いのかな。
'''



import collections
import itertools
import operator

class UnionFind:
    def __init__(self, elems=None):
        class KeyDict(dict):  # dict + キーがないときは dict[key] = key として key を返す。
            def __missing__(self, key):
                self[key] = key
                return key

        self.parent = KeyDict()  # キーがなければ孤立点→自身が key（parent[key]=key）
        self.rank = collections.defaultdict(int)  # 木の高さ。デフォルトでは 0。経路圧縮すると本来は rank が下がるが、その効果は無視している。
        self.size_ = collections.defaultdict(lambda: 1)  # 根に属する頂点の個数。根の値しか信用できない。

        if elems is not None:
            for elem in elems:
                _, _ = self.parent[elem], self.rank[elem]

    def find(self, x):  # あるキーの根を探す。
        if self.parent[x] == x:
            return x  # 自身が根なら自身を返す。
        else:
            self.parent[x] = self.find(self.parent[x])  # 再帰的に親を探して、すべて根に付け替える（経路圧縮）。
            return self.parent[x]

    def unite(self, x, y):
        x = self.find(x)  # x の根を求める。
        y = self.find(y)  # y の根を求める。
        if x == y:  # もともと同一の木なら何もしない。
            return
        if self.rank[x] < self.rank[y]:  # rank （木の高さ）の低いほうを高いほうに繋げたい。
            x, y = y, x
        self.parent[y] = x  # x の親を y にする。
        self.size_[x] += self.size_[y]
        if self.rank[x] == self.rank[y]:  # 同じ高さの木だったら片方の rank を1つ上げておく。
            self.rank[x] += 1

    def are_same(self, x, y):
        return self.find(x) == self.find(y)

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

    def grouper(self):
        roots = [(x, self.find(x_par)) for x, x_par in self.parent.items()]
        root = operator.itemgetter(1)
        for _, group in itertools.groupby(sorted(roots, key=root), root):
            yield [x for x, _ in group]


def main(): 
    N, M = LI()

    visited = [False]*(N+1)
    uf = UnionFind()

    ans_rev = [0]*(M+1)
    ans_rev[0] = N * (N-1) // 2  # N_C_2
    A_li, B_li = [], []
    for i in range(0, M):
        A, B = LI()
        A_li.append(A)
        B_li.append(B)

    for i, (A, B) in enumerate(reversed(list(zip(A_li, B_li)))):
        if not uf.are_same(A, B):
            ans_rev[i+1] = ans_rev[i] - uf.size(A) * uf.size(B)
        else:
            ans_rev[i+1] = ans_rev[i]
        uf.unite(A, B)

    for i in reversed(ans_rev[:-1]):
        print(i)


main() import sys sys sys.setrecursionlimit(10**7) sys.setrecursionlimit sys setrecursionlimit 10**7 10 7 int(x) for x in sys.stdin.readline().split() for x in sys.stdin.readline().split() x sys.stdin.readline().split() sys.stdin.readline().split sys.stdin.readline() sys.stdin.readline sys.stdin sys stdin readline split for x in sys.stdin.readline().split() int(x) int x def LI(): return [int(x) for x in sys.stdin.readline().split()] LI int(x) - 1 for x in sys.stdin.readline().split() for x in sys.stdin.readline().split() x sys.stdin.readline().split() sys.stdin.readline().split sys.stdin.readline() sys.stdin.readline sys.stdin sys stdin readline split for x in sys.stdin.readline().split() int(x) - 1 int(x) int x 1 def LI_(): return [int(x) - 1 for x in sys.stdin.readline().split()] LI_ float(x) for x in sys.stdin.readline().split() for x in sys.stdin.readline().split() x sys.stdin.readline().split() sys.stdin.readline().split sys.stdin.readline() sys.stdin.readline sys.stdin sys stdin readline split for x in sys.stdin.readline().split() float(x) float x def LF(): return [float(x) for x in sys.stdin.readline().split()] LF def LS(): return sys.stdin.readline().split() LS def II(): return int(sys.stdin.readline()) II def SI(): return sys.stdin.readline().strip() SI LI() for _ in range(n) for _ in range(n) _ range(n) range n for _ in range(n) LI() LI def LILI(n): return [LI() for _ in range(n)] LILI n n INF = 10 ** 18 INF 10 ** 18 10 18 MOD = 10 ** 9 + 7 MOD 10 ** 9 + 7 10 ** 9 10 9 7 '''
独立なグラフがいくつかできていく。
求めるもの：グラフ1にある頂点と2にある頂点の combinaton, 1と3のcombination,……2と3のcombination…… の総和を求めるのを繰り返す。
union-findで行けそうだがN, Mどちらも10**5なので間に合わなさそう。

最終的にはすべて独立となり、答えはnC2となる。
ということで nC2 からはじめてどんどん union していけばいいのでは。
union する2つのグループの頂点数の積を引いていけば良いのかな。
''' import collections collections import itertools itertools import operator operator class UnionFind:
    def __init__(self, elems=None):
        class KeyDict(dict):  # dict + キーがないときは dict[key] = key として key を返す。
            def __missing__(self, key):
                self[key] = key
                return key

        self.parent = KeyDict()  # キーがなければ孤立点→自身が key（parent[key]=key）
        self.rank = collections.defaultdict(int)  # 木の高さ。デフォルトでは 0。経路圧縮すると本来は rank が下がるが、その効果は無視している。
        self.size_ = collections.defaultdict(lambda: 1)  # 根に属する頂点の個数。根の値しか信用できない。

        if elems is not None:
            for elem in elems:
                _, _ = self.parent[elem], self.rank[elem]

    def find(self, x):  # あるキーの根を探す。
        if self.parent[x] == x:
            return x  # 自身が根なら自身を返す。
        else:
            self.parent[x] = self.find(self.parent[x])  # 再帰的に親を探して、すべて根に付け替える（経路圧縮）。
            return self.parent[x]

    def unite(self, x, y):
        x = self.find(x)  # x の根を求める。
        y = self.find(y)  # y の根を求める。
        if x == y:  # もともと同一の木なら何もしない。
            return
        if self.rank[x] < self.rank[y]:  # rank （木の高さ）の低いほうを高いほうに繋げたい。
            x, y = y, x
        self.parent[y] = x  # x の親を y にする。
        self.size_[x] += self.size_[y]
        if self.rank[x] == self.rank[y]:  # 同じ高さの木だったら片方の rank を1つ上げておく。
            self.rank[x] += 1

    def are_same(self, x, y):
        return self.find(x) == self.find(y)

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

    def grouper(self):
        roots = [(x, self.find(x_par)) for x, x_par in self.parent.items()]
        root = operator.itemgetter(1)
        for _, group in itertools.groupby(sorted(roots, key=root), root):
            yield [x for x, _ in group] UnionFind def __init__(self, elems=None):
        class KeyDict(dict):  # dict + キーがないときは dict[key] = key として key を返す。
            def __missing__(self, key):
                self[key] = key
                return key

        self.parent = KeyDict()  # キーがなければ孤立点→自身が key（parent[key]=key）
        self.rank = collections.defaultdict(int)  # 木の高さ。デフォルトでは 0。経路圧縮すると本来は rank が下がるが、その効果は無視している。
        self.size_ = collections.defaultdict(lambda: 1)  # 根に属する頂点の個数。根の値しか信用できない。

        if elems is not None:
            for elem in elems:
                _, _ = self.parent[elem], self.rank[elem]

     __init__ class KeyDict(dict):  # dict + キーがないときは dict[key] = key として key を返す。
            def __missing__(self, key):
                self[key] = key
                return key

         KeyDict def __missing__(self, key):
                self[key] = key
                return key

         __missing__ self[key] = key self[key] self key key return key key self self key key dict self.parent = KeyDict() self.parent self parent KeyDict() KeyDict self.rank = collections.defaultdict(int) self.rank self rank collections.defaultdict(int) collections.defaultdict collections defaultdict int self.size_ = collections.defaultdict(lambda: 1) self.size_ self size_ collections.defaultdict(lambda: 1) collections.defaultdict collections defaultdict lambda: 1 1 if elems is not None:
            for elem in elems:
                _, _ = self.parent[elem], self.rank[elem]

     elems is not None elems None for elem in elems:
                _, _ = self.parent[elem], self.rank[elem]

     elem elems _, _ = self.parent[elem], self.rank[elem] _ self.parent[elem] self.parent self parent elem _ self.rank[elem] self.rank self rank elem self self elems=None elems None def find(self, x):  # あるキーの根を探す。
        if self.parent[x] == x:
            return x  # 自身が根なら自身を返す。
        else:
            self.parent[x] = self.find(self.parent[x])  # 再帰的に親を探して、すべて根に付け替える（経路圧縮）。
            return self.parent[x]

     find if self.parent[x] == x:
            return x  # 自身が根なら自身を返す。
        else:
            self.parent[x] = self.find(self.parent[x])  # 再帰的に親を探して、すべて根に付け替える（経路圧縮）。
            return self.parent[x]

     self.parent[x] == x self.parent[x] self.parent self parent x x return x x self.parent[x] = self.find(self.parent[x]) self.parent[x] self.parent self parent x self.find(self.parent[x]) self.find self find self.parent[x] self.parent self parent x return self.parent[x] self.parent[x] self.parent self parent x self self x x def unite(self, x, y):
        x = self.find(x)  # x の根を求める。
        y = self.find(y)  # y の根を求める。
        if x == y:  # もともと同一の木なら何もしない。
            return
        if self.rank[x] < self.rank[y]:  # rank （木の高さ）の低いほうを高いほうに繋げたい。
            x, y = y, x
        self.parent[y] = x  # x の親を y にする。
        self.size_[x] += self.size_[y]
        if self.rank[x] == self.rank[y]:  # 同じ高さの木だったら片方の rank を1つ上げておく。
            self.rank[x] += 1

     unite x = self.find(x) x self.find(x) self.find self find x y = self.find(y) y self.find(y) self.find self find y if x == y:  # もともと同一の木なら何もしない。
            return
         x == y x y return if self.rank[x] < self.rank[y]:  # rank （木の高さ）の低いほうを高いほうに繋げたい。
            x, y = y, x
         self.rank[x] < self.rank[y] self.rank[x] self.rank self rank x self.rank[y] self.rank self rank y x, y = y, x x y y x self.parent[y] = x self.parent[y] self.parent self parent y x self.size_[x] += self.size_[y] self.size_[x] self.size_ self size_ x self.size_[y] self.size_ self size_ y if self.rank[x] == self.rank[y]:  # 同じ高さの木だったら片方の rank を1つ上げておく。
            self.rank[x] += 1

     self.rank[x] == self.rank[y] self.rank[x] self.rank self rank x self.rank[y] self.rank self rank y self.rank[x] += 1 self.rank[x] self.rank self rank x 1 self self x x y y def are_same(self, x, y):
        return self.find(x) == self.find(y)

     are_same return self.find(x) == self.find(y) self.find(x) == self.find(y) self.find(x) self.find self find x self.find(y) self.find self find y self self x x y y def size(self, x):
        return self.size_[self.find(x)]

     size return self.size_[self.find(x)] self.size_[self.find(x)] self.size_ self size_ self.find(x) self.find self find x self self x x def grouper(self):
        roots = [(x, self.find(x_par)) for x, x_par in self.parent.items()]
        root = operator.itemgetter(1)
        for _, group in itertools.groupby(sorted(roots, key=root), root):
            yield [x for x, _ in group] grouper (x, self.find(x_par)) for x, x_par in self.parent.items() for x, x_par in self.parent.items() x x_par self.parent.items() self.parent.items self.parent self parent items for x, x_par in self.parent.items() (x, self.find(x_par)) x self.find(x_par) self.find self find x_par roots = [(x, self.find(x_par)) for x, x_par in self.parent.items()] roots [(x, self.find(x_par)) for x, x_par in self.parent.items()] root = operator.itemgetter(1) root operator.itemgetter(1) operator.itemgetter operator itemgetter 1 for _, group in itertools.groupby(sorted(roots, key=root), root):
            yield [x for x, _ in group] _ group itertools.groupby(sorted(roots, key=root), root) itertools.groupby itertools groupby sorted(roots, key=root) sorted roots key=root key root root x for x, _ in group for x, _ in group x _ group for x, _ in group x yield [x for x, _ in group] [x for x, _ in group] self self def main(): 
    N, M = LI()

    visited = [False]*(N+1)
    uf = UnionFind()

    ans_rev = [0]*(M+1)
    ans_rev[0] = N * (N-1) // 2  # N_C_2
    A_li, B_li = [], []
    for i in range(0, M):
        A, B = LI()
        A_li.append(A)
        B_li.append(B)

    for i, (A, B) in enumerate(reversed(list(zip(A_li, B_li)))):
        if not uf.are_same(A, B):
            ans_rev[i+1] = ans_rev[i] - uf.size(A) * uf.size(B)
        else:
            ans_rev[i+1] = ans_rev[i]
        uf.unite(A, B)

    for i in reversed(ans_rev[:-1]):
        print(i) main N, M = LI() N LI() LI M visited = [False]*(N+1) visited [False]*(N+1) [False] False N+1 N 1 uf = UnionFind() uf UnionFind() UnionFind ans_rev = [0]*(M+1) ans_rev [0]*(M+1) [0] 0 M+1 M 1 ans_rev[0] = N * (N-1) // 2 ans_rev[0] ans_rev 0 N * (N-1) // 2 N * (N-1) N N-1 N 1 2 A_li, B_li = [], [] A_li [] B_li [] for i in range(0, M):
        A, B = LI()
        A_li.append(A)
        B_li.append(B)

     i range(0, M) range 0 M A, B = LI() A LI() LI B A_li.append(A) A_li.append A_li append A B_li.append(B) B_li.append B_li append B for i, (A, B) in enumerate(reversed(list(zip(A_li, B_li)))):
        if not uf.are_same(A, B):
            ans_rev[i+1] = ans_rev[i] - uf.size(A) * uf.size(B)
        else:
            ans_rev[i+1] = ans_rev[i]
        uf.unite(A, B)

     i (A, B) A B enumerate(reversed(list(zip(A_li, B_li)))) enumerate reversed(list(zip(A_li, B_li))) reversed list(zip(A_li, B_li)) list zip(A_li, B_li) zip A_li B_li if not uf.are_same(A, B):
            ans_rev[i+1] = ans_rev[i] - uf.size(A) * uf.size(B)
        else:
            ans_rev[i+1] = ans_rev[i]
         not uf.are_same(A, B) uf.are_same(A, B) uf.are_same uf are_same A B ans_rev[i+1] = ans_rev[i] - uf.size(A) * uf.size(B) ans_rev[i+1] ans_rev i+1 i 1 ans_rev[i] - uf.size(A) * uf.size(B) ans_rev[i] ans_rev i uf.size(A) * uf.size(B) uf.size(A) uf.size uf size A uf.size(B) uf.size uf size B ans_rev[i+1] = ans_rev[i] ans_rev[i+1] ans_rev i+1 i 1 ans_rev[i] ans_rev i uf.unite(A, B) uf.unite uf unite A B for i in reversed(ans_rev[:-1]):
        print(i) i reversed(ans_rev[:-1]) reversed ans_rev[:-1] ans_rev :-1 -1 print(i) print i main() main def LILI(n): return [LI() for _ in range(n)] def LILI(n): return [LI() for _ in range(n)] LILI def LF(): return [float(x) for x in sys.stdin.readline().split()] def LF(): return [float(x) for x in sys.stdin.readline().split()] LF def SI(): return sys.stdin.readline().strip() def SI(): return sys.stdin.readline().strip() SI INF = 10 ** 18 10 ** 18 INF MOD = 10 ** 9 + 7 10 ** 9 + 7 MOD class UnionFind:
    def __init__(self, elems=None):
        class KeyDict(dict):  # dict + キーがないときは dict[key] = key として key を返す。
            def __missing__(self, key):
                self[key] = key
                return key

        self.parent = KeyDict()  # キーがなければ孤立点→自身が key（parent[key]=key）
        self.rank = collections.defaultdict(int)  # 木の高さ。デフォルトでは 0。経路圧縮すると本来は rank が下がるが、その効果は無視している。
        self.size_ = collections.defaultdict(lambda: 1)  # 根に属する頂点の個数。根の値しか信用できない。

        if elems is not None:
            for elem in elems:
                _, _ = self.parent[elem], self.rank[elem]

    def find(self, x):  # あるキーの根を探す。
        if self.parent[x] == x:
            return x  # 自身が根なら自身を返す。
        else:
            self.parent[x] = self.find(self.parent[x])  # 再帰的に親を探して、すべて根に付け替える（経路圧縮）。
            return self.parent[x]

    def unite(self, x, y):
        x = self.find(x)  # x の根を求める。
        y = self.find(y)  # y の根を求める。
        if x == y:  # もともと同一の木なら何もしない。
            return
        if self.rank[x] < self.rank[y]:  # rank （木の高さ）の低いほうを高いほうに繋げたい。
            x, y = y, x
        self.parent[y] = x  # x の親を y にする。
        self.size_[x] += self.size_[y]
        if self.rank[x] == self.rank[y]:  # 同じ高さの木だったら片方の rank を1つ上げておく。
            self.rank[x] += 1

    def are_same(self, x, y):
        return self.find(x) == self.find(y)

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

    def grouper(self):
        roots = [(x, self.find(x_par)) for x, x_par in self.parent.items()]
        root = operator.itemgetter(1)
        for _, group in itertools.groupby(sorted(roots, key=root), root):
            yield [x for x, _ in group] class UnionFind:
    def __init__(self, elems=None):
        class KeyDict(dict):  # dict + キーがないときは dict[key] = key として key を返す。
            def __missing__(self, key):
                self[key] = key
                return key

        self.parent = KeyDict()  # キーがなければ孤立点→自身が key（parent[key]=key）
        self.rank = collections.defaultdict(int)  # 木の高さ。デフォルトでは 0。経路圧縮すると本来は rank が下がるが、その効果は無視している。
        self.size_ = collections.defaultdict(lambda: 1)  # 根に属する頂点の個数。根の値しか信用できない。

        if elems is not None:
            for elem in elems:
                _, _ = self.parent[elem], self.rank[elem]

    def find(self, x):  # あるキーの根を探す。
        if self.parent[x] == x:
            return x  # 自身が根なら自身を返す。
        else:
            self.parent[x] = self.find(self.parent[x])  # 再帰的に親を探して、すべて根に付け替える（経路圧縮）。
            return self.parent[x]

    def unite(self, x, y):
        x = self.find(x)  # x の根を求める。
        y = self.find(y)  # y の根を求める。
        if x == y:  # もともと同一の木なら何もしない。
            return
        if self.rank[x] < self.rank[y]:  # rank （木の高さ）の低いほうを高いほうに繋げたい。
            x, y = y, x
        self.parent[y] = x  # x の親を y にする。
        self.size_[x] += self.size_[y]
        if self.rank[x] == self.rank[y]:  # 同じ高さの木だったら片方の rank を1つ上げておく。
            self.rank[x] += 1

    def are_same(self, x, y):
        return self.find(x) == self.find(y)

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

    def grouper(self):
        roots = [(x, self.find(x_par)) for x, x_par in self.parent.items()]
        root = operator.itemgetter(1)
        for _, group in itertools.groupby(sorted(roots, key=root), root):
            yield [x for x, _ in group] UnionFind def LI(): return [int(x) for x in sys.stdin.readline().split()] def LI(): return [int(x) for x in sys.stdin.readline().split()] LI def main(): 
    N, M = LI()

    visited = [False]*(N+1)
    uf = UnionFind()

    ans_rev = [0]*(M+1)
    ans_rev[0] = N * (N-1) // 2  # N_C_2
    A_li, B_li = [], []
    for i in range(0, M):
        A, B = LI()
        A_li.append(A)
        B_li.append(B)

    for i, (A, B) in enumerate(reversed(list(zip(A_li, B_li)))):
        if not uf.are_same(A, B):
            ans_rev[i+1] = ans_rev[i] - uf.size(A) * uf.size(B)
        else:
            ans_rev[i+1] = ans_rev[i]
        uf.unite(A, B)

    for i in reversed(ans_rev[:-1]):
        print(i) def main(): 
    N, M = LI()

    visited = [False]*(N+1)
    uf = UnionFind()

    ans_rev = [0]*(M+1)
    ans_rev[0] = N * (N-1) // 2  # N_C_2
    A_li, B_li = [], []
    for i in range(0, M):
        A, B = LI()
        A_li.append(A)
        B_li.append(B)

    for i, (A, B) in enumerate(reversed(list(zip(A_li, B_li)))):
        if not uf.are_same(A, B):
            ans_rev[i+1] = ans_rev[i] - uf.size(A) * uf.size(B)
        else:
            ans_rev[i+1] = ans_rev[i]
        uf.unite(A, B)

    for i in reversed(ans_rev[:-1]):
        print(i) main def LS(): return sys.stdin.readline().split() def LS(): return sys.stdin.readline().split() LS def II(): return int(sys.stdin.readline()) def II(): return int(sys.stdin.readline()) II def LI_(): return [int(x) - 1 for x in sys.stdin.readline().split()] def LI_(): return [int(x) - 1 for x in sys.stdin.readline().split()] LI_
import sys
sys.setrecursionlimit(10**7)
def LI(): return [int(x) for x in sys.stdin.readline().split()]
def LI_(): return [int(x) - 1 for x in sys.stdin.readline().split()]
def LF(): return [float(x) for x in sys.stdin.readline().split()]
def LS(): return sys.stdin.readline().split()
def II(): return int(sys.stdin.readline())
def SI(): return sys.stdin.readline().strip()
def LILI(n): return [LI() for _ in range(n)]
INF = 10 ** 18
MOD = 10 ** 9 + 7


'''
独立なグラフがいくつかできていく。
求めるもの：グラフ1にある頂点と2にある頂点の combinaton, 1と3のcombination,……2と3のcombination…… の総和を求めるのを繰り返す。
union-findで行けそうだがN, Mどちらも10**5なので間に合わなさそう。

最終的にはすべて独立となり、答えはnC2となる。
ということで nC2 からはじめてどんどん union していけばいいのでは。
union する2つのグループの頂点数の積を引いていけば良いのかな。
'''



import collections
import itertools
import operator

class UnionFind:
    def __init__(self, elems=None):
        class KeyDict(dict):  # dict + キーがないときは dict[key] = key として key を返す。
            def __missing__(self, key):
                self[key] = key
                return key

        self.parent = KeyDict()  # キーがなければ孤立点→自身が key（parent[key]=key）
        self.rank = collections.defaultdict(int)  # 木の高さ。デフォルトでは 0。経路圧縮すると本来は rank が下がるが、その効果は無視している。
        self.size_ = collections.defaultdict(lambda: 1)  # 根に属する頂点の個数。根の値しか信用できない。

        if elems is not None:
            for elem in elems:
                _, _ = self.parent[elem], self.rank[elem]

    def find(self, x):  # あるキーの根を探す。
        if self.parent[x] == x:
            return x  # 自身が根なら自身を返す。
        else:
            self.parent[x] = self.find(self.parent[x])  # 再帰的に親を探して、すべて根に付け替える（経路圧縮）。
            return self.parent[x]

    def unite(self, x, y):
        x = self.find(x)  # x の根を求める。
        y = self.find(y)  # y の根を求める。
        if x == y:  # もともと同一の木なら何もしない。
            return
        if self.rank[x] < self.rank[y]:  # rank （木の高さ）の低いほうを高いほうに繋げたい。
            x, y = y, x
        self.parent[y] = x  # x の親を y にする。
        self.size_[x] += self.size_[y]
        if self.rank[x] == self.rank[y]:  # 同じ高さの木だったら片方の rank を1つ上げておく。
            self.rank[x] += 1

    def are_same(self, x, y):
        return self.find(x) == self.find(y)

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

    def grouper(self):
        roots = [(x, self.find(x_par)) for x, x_par in self.parent.items()]
        root = operator.itemgetter(1)
        for _, group in itertools.groupby(sorted(roots, key=root), root):
            yield [x for x, _ in group]


def main(): 
    N, M = LI()

    visited = [False]*(N+1)
    uf = UnionFind()

    ans_rev = [0]*(M+1)
    ans_rev[0] = N * (N-1) // 2  # N_C_2
    A_li, B_li = [], []
    for i in range(0, M):
        A, B = LI()
        A_li.append(A)
        B_li.append(B)

    for i, (A, B) in enumerate(reversed(list(zip(A_li, B_li)))):
        if not uf.are_same(A, B):
            ans_rev[i+1] = ans_rev[i] - uf.size(A) * uf.size(B)
        else:
            ans_rev[i+1] = ans_rev[i]
        uf.unite(A, B)

    for i in reversed(ans_rev[:-1]):
        print(i)


main()