CodeEff/ECCO huggingface dataset:
# Original solution
class UnionFind:
    def __init__(self, n):
        self.par = [i for i in range(n + 1)]
        self.rank = [0] * (n + 1)
        self.count = [1] * (n + 1)

    # find
    def find(self, x):
        if self.par[x] == x:
            return x
        else:
            self.par[x] = self.find(self.par[x])
            return self.par[x]

    # union
    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)
        if x != y:
            if self.rank[x] < self.rank[y]:
                self.par[x] = y
                self.count[y] += self.count[x]
            else:
                self.par[y] = x
                if self.rank[x] == self.rank[y]:
                    self.rank[x] += 1
                self.count[x] += self.count[y]

    # size
    def size(self, x):
        return self.count[self.par[x]]

    # same
    def issame(self, x, y):
        return self.find(x) == self.find(y)

n,m=list(map(int,input().split()))
a=[tuple(map(int,input().split())) for i in range(m)]
b = [(j,i)for j in range(1,n+1) for i in range(j+1,n+1)]
U=UnionFind(n)
dis=[0]
cn=0
for i,j in reversed(a):
    if U.issame(i,j)==True:
        dis.append(cn)
    else:
        cn+=U.size(i)*U.size(j)
        dis.append(cn)
    U.union(i, j)
M=int(n*(n-1)/2)
for x in reversed(dis[:-1]):
    print((M-x))

# Optimized solution
class UnionFind:
    def __init__(self, n):
        self.par = [i for i in range(n + 1)]
        self.rank = [0] * (n + 1)
        self.count = [1] * (n + 1)

    # find
    def find(self, x):
        if self.par[x] == x:
            return x
        else:
            self.par[x] = self.find(self.par[x])
            return self.par[x]

    # union
    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)
        if x != y:
            if self.rank[x] < self.rank[y]:
                self.par[x] = y
                self.count[y] += self.count[x]
            else:
                self.par[y] = x
                if self.rank[x] == self.rank[y]:
                    self.rank[x] += 1
                self.count[x] += self.count[y]

    # size
    def size(self, x):
        return self.count[self.par[x]]

    # same
    def issame(self, x, y):
        return self.find(x) == self.find(y)

n,m=list(map(int,input().split()))
a=[tuple(map(int,input().split())) for i in range(m)]
U=UnionFind(n)
dis=[0]
cn=0
for i,j in reversed(a):
    if U.issame(i,j)==True:
        dis.append(cn)
    else:
        cn+=U.size(i)*U.size(j)
        dis.append(cn)
    U.union(i, j)
M=int(n*(n-1)/2)
for x in reversed(dis[:-1]):
    print((M-x))
# Original solution
class UnionFind:
  def __init__(self,n):
    *self.table,=range(n)
    self.rank=[1]*n
  def root(self,x):
    if self.table[x]==x:
      return x
    else:
      self.table[x]=self.root(self.table[x])
      return self.table[x]
  def unite(self,x,y):
    x=self.root(x)
    y=self.root(y)
    if x!=y:
      if self.rank[x]<self.rank[y]:
        x,y=y,x
      self.rank[x]+=self.rank[y]
      self.table[y]=x
  def same(self,x,y):
    return self.root(x)==self.root(y)
  def size(self,x):
    return self.rank[self.root(x)]
(n,m),_,*e=[map(int,t.split())for t in open(0).readlines()]
u=UnionFind(n)
a=[n*~-n//2]
for x,y in e[::-1]:
  x,y=x-1,y-1
  t=a[-1]
  if not u.same(x,y):
    t-=u.size(x)*u.size(y)
  a.append(t)
  u.unite(x,y)
print(*a[::-1],sep='\n')


# Optimized solution
def root(x):
  if table[x]==x:
    return x
  else:
    table[x]=root(table[x])
    return table[x]
def unite(x,y):
  x=root(x)
  y=root(y)
  if x!=y:
    if rank[x]<rank[y]:
      x,y=y,x
    rank[x]+=rank[y]
    table[y]=x
def same(x,y):
  return root(x)==root(y)
def size(x):
  return rank[root(x)]
(n,m),_,*e=[map(int,t.split())for t in open(0).readlines()]
*table,=range(n)
rank=[1]*n
a=[n*~-n//2]
for x,y in e[::-1]:
  x,y=x-1,y-1
  t=a[-1]
  if not same(x,y):
    t-=size(x)*size(y)
  a.append(t)
  unite(x,y)
print(*a[::-1],sep='\n')
# Original solution
def r(x):
  while u[x]>=0:x=u[x]
  return x
(n,m),_,*e=[map(int,t.split())for t in open(0).readlines()]
u=[-1]*n
a=[n*~-n//2]
for x,y in e[::-1]:
  t=a[-1]
  x,y=r(x-1),r(y-1)
  if x!=y:
    t-=u[x]*u[y]
    u[x]+=u[y]
    u[y]=x
  a.append(t)
print(*a[::-1],sep='\n')


# Optimized solution
def r(x):
  while u[x]>=0:x=u[x]
  return x
(n,m),_,*e=[map(int,t.split())for t in open(0).readlines()]
u=[-1]*n
a=[n*~-n//2]
for x,y in e[::-1]:
  t=a[-1]
  x,y=sorted((r(x-1),r(y-1)))
  if x!=y:
    t-=u[x]*u[y]
    u[x]+=u[y]
    u[y]=x
  a.append(t)
print(*a[::-1],sep='\n')
# Original solution
def r(x):
  while u[x]>=0:x=u[x]
  return x
(n,m),_,*e=[map(int,t.split())for t in open(0).readlines()]
u=[-1]*n
a=[n*~-n//2]
for x,y in e[::-1]:
  t=a[-1]
  x,y=sorted((r(x-1),r(y-1)))
  if x!=y:
    t-=u[x]*u[y]
    u[x]+=u[y]
    u[y]=x
  a.append(t)
print(*a[::-1],sep='\n')


# Optimized solution
def r(x):
 while u[x]>=0:x=u[x]
 return x
(n,m),_,*e=[map(int,t.split())for t in open(0).readlines()];u=[-1]*-~n;a=[n*~-n//2]
for x,y in e[::-1]:
 t=a[-1];x,y=sorted((r(x),r(y)))
 if x!=y:t-=u[x]*u[y];u[x]+=u[y];u[y]=x
 a.append(t)
print(*a[::-1],sep='\n')
# Original solution
r=lambda x:x if u[x]<0else r(u[x])
(n,m),_,*e=[map(int,t.split())for t in open(0).readlines()]
u=[-1]*-~n
a=[n*~-n//2]
for x,y in e[::-1]:
 t=a[-1];x,y=sorted((r(x),r(y)))
 if x!=y:t-=u[x]*u[y];u[x]+=u[y];u[y]=x
 a+=[t]
print(*a[::-1],sep='\n')


# Optimized solution
r=lambda x:x if u[x]<0else r(u[x])
(n,m),_,*e=[map(int,t.split())for t in open(0).readlines()]
u=[-1]*-~n
t=n*~-n//2
a=[t]
for x,y in e[::-1]:
 x,y=sorted((r(x),r(y)))
 if x!=y:t-=u[x]*u[y];u[x]+=u[y];u[y]=x
 a+=[t]
print(*a[::-1],sep='\n')
# Original solution
r=lambda x:x if u[x]<0else r(u[x])
(n,m),_,*e=[map(int,t.split())for t in open(0).readlines()]
u=[-1]*-~n
t=n*~-n//2
a=[t]
for x,y in e[::-1]:
 x,y=sorted((r(x),r(y)))
 if x!=y:t-=u[x]*u[y];u[x]+=u[y];u[y]=x
 a+=[t]
print(*a[::-1],sep='\n')


# Optimized solution
r=lambda x:x if u[x]<0else r(u[x])
n,m,*e=map(int,open(0).read().split())
u=[-1]*-~n
t=n*~-n//2
a=[t]
for x,y in zip(e[-2:1:-2],e[:1:-2]):
 x,y=sorted((r(x),r(y)))
 if x!=y:t-=u[x]*u[y];u[x]+=u[y];u[y]=x
 a+=[t]
print(*a[::-1],sep='\n')
# Original solution
r=lambda x:x if u[x]<0else r(u[x])
n,m,*e=map(int,open(0).read().split())
u=[-1]*-~n
t=n*~-n//2
a=t,
for x,y in zip(e[-2:1:-2],e[:1:-2]):
 x,y=sorted((r(x),r(y)))
 if x!=y:t-=u[x]*u[y];u[x]+=u[y];u[y]=x
 a+=t,
print(*a[::-1],sep='\n')


# Optimized solution
r=lambda x:x if u[x]<0else r(u[x])
n,m,*e=map(int,open(0).read().split())
u=[-1]*-~n
t=n*~-n//2
a=[t]
for x,y in zip(e[-2:1:-2],e[:1:-2]):
 x,y=sorted((r(x),r(y)))
 if x!=y:t-=u[x]*u[y];u[x]+=u[y];u[y]=x
 a+=t,
print(*a[::-1],sep='\n')
# Original solution
N, M =list(map(int, input().split()))
a, b = [], []
for i in range(M):
    a_i, b_i = list(map(int, input().split()))
    a.append(a_i -1)
    b.append(b_i -1)
    
class UnionFind:
    def __init__(self, n):
        self.p = {}
        self.r = {}
        self.s = {}
        for i in range(n):
            self.p[i] = i # 親,グループ番号
            self.r[i] = 1 
            self.s[i] = 1 # サイズ、連結数
            
    def find(self, n):
        path = []
        while n != self.p[n]:
            path.append(n)
            n = self.p[n]
        for m in path:
            self.p[m] = n
        return n 

    def union(self, a, b):
        ap = self.find(a)
        bp = self.find(b)
        if self.r[ap] > self.r[bp]:
            self.p[bp] = ap
            size = self.s[bp] + self.s[ap]
            self.s[ap] = size
        elif self.r[bp] > self.r[ap]:
            self.p[ap] = bp
            size = self.s[bp] + self.s[ap]
            self.s[bp] = size
        elif ap != bp:
            self.p[ap] = bp
            self.r[bp] += 1
            size = self.s[bp] + self.s[ap]
            self.s[bp] = size
            
    def size(self, n):
        np = self.find(n)
        return self.s[np]
    
s = UnionFind(N)
 
ans = (N*(N-1)) // 2
anss = [ans]

for i in range(M):
    j = M - i - 1
    if s.find(a[j]) != s.find(b[j]):
        ans -= s.size(a[j]) * s.size(b[j])
        s.union(a[j], b[j])
    if i != M-1:
        anss.append(ans)
        
sort_ans = sorted(anss)
for i in range(len(sort_ans)):
    print((sort_ans[i]))

# Optimized solution
class UnionFind:
    def __init__(self, n):
        self.p = [i for i in range(n)]
        self.s = [1 for i in range(n)]
            
    def find(self, x):
        if x == self.p[x]:
            return x
        else:
            self.p[x] = self.find(self.p[x])
            return self.p[x]

    def union(self, a, b):
        ap = self.find(a)
        bp = self.find(b)
        if ap != bp:
            mini = min(ap ,bp)
            maxi = max(ap, bp)
            self.p[maxi] = mini
            size = self.s[bp] + self.s[ap]
            self.s[bp] = size
            self.s[ap] = size
            return True
        else:
            return False
            
    def size(self, n):
        np = self.find(n)
        return self.s[np]
    
N, M =list(map(int, input().split()))
a, b = [], []
for i in range(M):
    a_i, b_i = list(map(int, input().split()))
    a.append(a_i -1)
    b.append(b_i - 1)
    
s = UnionFind(N)
 
ans = (N*(N-1)) // 2
anss = [ans]
    
for i in range(M):
    j = M - i - 1
    if s.find(a[j]) != s.find(b[j]):
        ans -= s.size(a[j]) * s.size(b[j])
        s.union(a[j], b[j])
    if i != M-1:
        anss.append(ans)
        
sort_ans = sorted(anss)
for i in range(len(sort_ans)):
    print((sort_ans[i]))
# Original solution
# coding:utf-8

import sys
from collections import deque, defaultdict

INF = float('inf')
MOD = 10 ** 9 + 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 LS(): return sys.stdin.readline().split()
def II(): return int(sys.stdin.readline())
def SI(): return input()


# UnionFind
class UnionFind:
    # 負: ルートで値は木のサイズを表す
    # 正: 子で値は次のノードを表す
    def __init__(self, size: int) -> None:
        self.nodes = [-1] * size  # 初期状態では全てのノードがルート

    def find(self, x: int) -> int:
        if self.nodes[x] < 0:
            return x
        else:
            self.nodes[x] = self.find(self.nodes[x])
            return self.nodes[x]

    def unite(self, x: int, y: int) -> bool:
        rx, ry = self.find(x), self.find(y)
        if rx == ry:
            return False

        if self.size(x) < self.size(y):
            rx, ry = ry, rx

        self.nodes[rx] += self.nodes[ry]
        self.nodes[ry] = rx

        return True

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

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


def main():
    n, m = LI()

    uf = UnionFind(n)
    res = deque()
    res.append(n * (n - 1) // 2)
    AB = [LI_() for _ in range(m)]
    AB.reverse()
    for a, b in AB:
        if uf.same(a, b):
            res.append(res[-1])
            continue

        tmp = res[-1] - uf.size(a) * uf.size(b)
        if tmp < 0:
            tmp = 0
        res.append(tmp)
        uf.unite(a, b)

    res.reverse()
    res.popleft()
    print(*res, sep='\n')


main()


# Optimized solution
# coding:utf-8

import sys

INF = float('inf')
MOD = 10 ** 9 + 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 LS(): return sys.stdin.readline().split()
def II(): return int(sys.stdin.readline())
def SI(): return input()


def main():
    n, m = LI()
    AB = [LI_() for _ in range(m)]
    AB.reverse()

    # マージテク
    par = [i for i in range(n)]  # 連結しているグループのリーダ
    node = [set([i]) for i in range(n)]  # グループに所属しているノードの集合

    def merge(x, y):
        # 既に連結済みなら何もしない
        if par[x] == par[y]:
            return 0
        x, y = par[x], par[y]
        size_x, size_y = len(node[x]), len(node[y])

        # 小さいグループを大きいグループにマージする. O(log(n))
        if size_x < size_y:
            x, y = y, x

        # ノードの移動とリーダの更新
        while node[y]:
            v = node[y].pop()
            par[v] = x
            node[x].add(v)

        # (xの連結数)*(yの連結数)だけ不満度が解消される
        return size_x * size_y

    res = [n * (n - 1) // 2]
    for a, b in AB:
        sub = merge(a, b)
        res.append(res[-1] - sub)

    res.pop()
    res.reverse()

    print(*res, sep='\n')


main()
# Original solution
N,M = list(map(int,input().split()))
AB = [list(map(int,input().split())) for _ in range(M)]
AB.reverse()    # 逆から考える

ans_list = [-1 for i in range(M)]
ans_list[M-1] = N*(N-1)//2

par = [i for i in range(N+1)]
rank = [0 for i in range(N+1)]
cnt = [1 for i in range(N+1)]

# 木の根を求める
def root(x):
    if par[x] == x:             # 根の時
        return x
    else:
        par[x] = root(par[x])   # 経路圧縮
        return par[x]

# xとyの属する集合を併合(ランク有)
def unite(x,y):
    x = root(x)
    y = root(y)
    if x == y:
        return
    if rank[x] < rank[y]:
        par[x] = y
        cnt[y] += cnt[x]
    else:
        par[y] = x
        cnt[x] += cnt[y]
        if rank[x] == rank[y]:
            rank[x] += 1

for i in range(M-1):
    a,b = AB[i][0],AB[i][1]
    # 答えはans_list[M-2-i]を書き換える
    if root(a) == root(b):
        ans_list[M-2-i] = ans_list[M-1-i]
        continue
    else:
        ans_list[M-2-i] = ans_list[M-1-i] - cnt[root(a)]*cnt[root(b)]
        unite(a,b)
print(('\n'.join(map(str,ans_list))))



# Optimized solution
# cnt->size
N,M = list(map(int,input().split()))
AB = [list(map(int,input().split())) for _ in range(M)]
AB.reverse()    # 逆から考える

ans_list = [-1 for i in range(M)]
ans_list[M-1] = N*(N-1)//2

# parent(親)
par = [i for i in range(N+1)]
# rank(深さ)
rank = [0 for i in range(N+1)]
# 同グループの頂点数
size = [1 for i in range(N+1)]

# 木の根を求める
def root(x):
    if par[x] == x:             # 根の時
        return x
    else:
        par[x] = root(par[x])   # 経路圧縮
        return par[x]

# xとyの属する集合を併合(ランク有)
def unite(x,y):
    x = root(x)
    y = root(y)
    if x == y:
        return
    if rank[x] < rank[y]:
        par[x] = y
        size[y] += size[x]
    else:
        par[y] = x
        size[x] += size[y]
        if rank[x] == rank[y]:
            rank[x] += 1

for i in range(M-1):
    a,b = AB[i][0],AB[i][1]
    # 答えはans_list[M-2-i]を書き換える
    if root(a) == root(b):
        ans_list[M-2-i] = ans_list[M-1-i]
        continue
    else:
        ans_list[M-2-i] = ans_list[M-1-i] - size[root(a)]*size[root(b)]
        unite(a,b)
print(('\n'.join(map(str,ans_list))))
# Original solution
class UnionFind(object):
    def __init__(self, n=1):
        self.par = [i for i in range(n)]
        self.rank = [0 for _ in range(n)]
        self.size = [1 for _ in range(n)]

    def find(self, x):
        """
        x が属するグループを探索
        """
        # xが根だったら自分自身をreturnする
        if self.par[x] == x:
            return x
        else:
            self.par[x] = self.find(self.par[x])
            return self.par[x]

    def union(self, x, y):
        """
        x と y のグループを結合
        """
        x = self.find(x)
        y = self.find(y)
        if x != y:
            if self.rank[x] < self.rank[y]:
                x, y = y, x
            if self.rank[x] == self.rank[y]:
                self.rank[x] += 1
            self.par[y] = x
            self.size[x] += self.size[y]

    def is_same(self, x, y):
        """
        x と y が同じグループかどうか
        """
        return self.find(x) == self.find(y)

    def get_size(self, x):
        """
        x が属するグループの要素数
        """
        x = self.find(x)
        return self.size[x]


def main():
    N, M = list(map(int, input().split()))

    A = [None] * M
    B = [None] * M

    for i in range(M):
        A[i], B[i] = list(map(int, input().split()))

    uni = UnionFind(N)
    ans = [None] * M
    ans[M-1] = N * (N - 1) // 2
    for i in range(M - 1, 0, -1):
        x = A[i] - 1
        y = B[i] - 1
        # 辺でつなげても元々同じグループに属していた場合、変化はしない
        if uni.is_same(x, y):
            ans[i - 1] = ans[i]
        else:
            ans[i - 1] = ans[i] - (uni.get_size(x) * uni.get_size(y))
        uni.union(x, y)


    for a in ans:
        print(a)

main()

# Optimized solution
class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n

    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return
        
        if self.parents[x] > self.parents[y]:
            x, y = y, x

        self.parents[x] += self.parents[y]
        self.parents[y] = x

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

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

def main():
    N, M = list(map(int, input().split()))
    bridges = []
    for _ in range(M):
        A, B = list(map(int, input().split()))
        A -= 1
        B -= 1
        bridges.append((A,B))

    ans = [0] * M
    ans[M-1] = N * (N - 1) // 2
    uni = UnionFind(N)
    for i in range(M-1,0,-1):
        a, b = bridges[i]
        if not uni.same(a, b):
            ans[i-1] = ans[i] - (uni.size(a) * uni.size(b))           
            uni.union(a,b)
        else:
            ans[i-1] = ans[i]
    for a in ans:
        print(a)

if __name__ == "__main__":
    main()
# Original solution
class UnionFind:
    def __init__(self, n):
        self.n = n
        self.parent = [i for i in range(n)]  # 親
        self.rank = [1] * n  # 木の高さ
        self.size = [1] * n  # size[i] は i を根とするグループのサイズ

    def find(self, x):  # 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, y の属する集合を併合する
        x = self.find(x)
        y = self.find(y)
        if x != y:
            if self.rank[x] < self.rank[y]:
                self.parent[x] = y
                self.size[y] += self.size[x]
            else:
                self.parent[y] = x
                self.size[x] += self.size[y]
                if self.rank[x] == self.rank[y]:
                    self.rank[x] += 1

    def is_same(self, x, y):  # x, y が同じ集合に属するか判定する
        return self.find(x) == self.find(y)

    def group_size(self, x):  # x が属する集合の大きさを返す
        return self.size[self.find(x)]

    def __str__(self):  # print 表示用
        return '\n'.join('{}: {}'.format(r, self.group_members(r)) for r in self.roots())


N, M = list(map(int, input().split()))
bridge = [list(map(int, input().split())) for _ in range(M)]
ans = [0]*M

uf = UnionFind(N)

# うしろからつないでいく
ans[M-1] = N*(N-1)//2
for i in range(1, M):
    A, B = bridge[M-i]

    if uf.is_same(A-1, B-1):
        ans[M-i-1] = ans[M-i]
    else:
        ans[M-i-1] = ans[M-i] - uf.group_size(A-1) * uf.group_size(B-1)

    uf.unite(A-1, B-1)


for a in ans:
    print(a)


# Optimized solution
class UnionFind:
    def __init__(self, n):
        self.n = n
        self.parent = [i for i in range(n)]  # 親
        self.rank = [1] * n  # 木の高さ
        self.size = [1] * n  # size[i] は i を根とするグループのサイズ

    def find(self, x):  # 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, y の属する集合を併合する
        x = self.find(x)
        y = self.find(y)
        if x != y:
            if self.rank[x] < self.rank[y]:
                self.parent[x] = y
                self.size[y] += self.size[x]
            else:
                self.parent[y] = x
                self.size[x] += self.size[y]
                if self.rank[x] == self.rank[y]:
                    self.rank[x] += 1

    def is_same(self, x, y):  # x, y が同じ集合に属するか判定する
        return self.find(x) == self.find(y)

    def group_size(self, x):  # x が属する集合の大きさを返す
        return self.size[self.find(x)]

    def __str__(self):  # print 表示用
        return '\n'.join('{}: {}'.format(r, self.group_members(r)) for r in self.roots())


N, M = list(map(int, input().split()))
bridge = [list(map(int, input().split())) for _ in range(M)]
ans = [0]*M
uf = UnionFind(N)

# うしろからつないでいく
ans[M-1] = N*(N-1)//2
for i in range(M-2, -1, -1):
    A, B = bridge[i+1]
    if uf.is_same(A-1, B-1):
        ans[i] = ans[i+1]
    else:
        ans[i] = ans[i+1] - uf.group_size(A-1) * uf.group_size(B-1)
    uf.unite(A-1, B-1)

for a in ans:
    print(a)
# Original solution
class unionfind:
	def __init__(self, n):
		self.parent = list(range(n))
	def find(self, i):
		if self.parent[i] != i: self.parent[i] = self.find(self.parent[i])
		return self.parent[i]
	def unite(self, i, j):
		i = self.find(i)
		j = self.find(j)
		if i != j: self.parent[i] = j
	def issame(self, i, j):
		return self.find(i) == self.find(j)
	def groups(self):
		r = list(range(len(self.parent)))
		return [[j for j in r if self.issame(j, i)] for i in r if i == self.parent[i]]
	@staticmethod
	def isconnected(l, u = None):
		nw, nh = len(l), len(l[0])
		rw, rh = list(range(nw)), list(range(nh))
		if not u: u = unionfind(nw * nh)
		f = -1
		for i in rw:
			for j in rh:
				if not l[i][j]: continue
				if f < 0: f = i + j * nw
				if j > 0 and l[i][j] == l[i][j - 1]: u.unite(i + j * nw, i + j * nw - nw)
				if i > 0 and l[i][j] == l[i - 1][j]: u.unite(i + j * nw, i + j * nw - 1)
		return f >= 0 and all([u.issame(f, i + j * nw) for i in rw for j in rh if l[i][j]])
	@staticmethod
	def isconnectedlist(nw, nh, lst):
		l = [[False] * nw for j in range(nh)]
		for i, j in lst: l[i][j] = True
		return unionfind.isconnected(l)
 
def culc(x):
    return int(x * (x-1) //2)
 
N , M = list(map(int, input().split()))
ans_lst = []
lst = []
for i in range(M):
    A , B = list(map( int , input().split()))
    lst.append([A-1 , B-1] )
cu = unionfind(N)
maxroute = culc(N)
temp = maxroute
for ls  in  reversed(lst):
    if temp == 0 :
        ans_lst.append(temp)
        continue
    temp = maxroute
    for subg in cu.groups():
        if len(subg) > 1:
            temp -= culc(len(subg))
    ans_lst.append(temp)
    cu.unite(ls[0] , ls[1])

for y in reversed(ans_lst):
    print(y)

# Optimized solution
#学習用
def root(x):
    # xが根であれば
    if x == p[x]:
        # xを返す
        return x
    # xが根でなければp[x]の根をp[x]に設定する
    p[x] = y = root(p[x])
    # p[x]の根を返却する
    return y
def unite(x, y):
    # xとyの根を取得する
    px = root(x); py = root(y)
    # xとyの根が同じならば
    if px == py:
        # 根が同じならば、0を返す
        return 0
    # yの根がxの根より大きければ、
    if px < py:
        # yの根の位置のp[]を、xの根にする
        p[py] = px
        # xの根の位置のサイズszを、yの根の位置のサイズだけ加算する
        sz[px] += sz[py]
    else:
    # yの根がxの根より小さければ
        # xの根の位置のp[]を、yの根を指すようにする
        p[px] = py
        sz[py] += sz[px]
    # 根が異なっていた＝結合した場合は、1を返却する
    return 1

N , M = list(map(int, input().split()))
*p , = list(range(N))
sz = [1] * N
E = []
for i in range(M):
    a , b = list(map(int, input().split()))
    E.append((a-1, b-1))

ans = [None] * M
E.reverse()
res = 0
ans[-1] = "%d\n" % (N*(N-1)//2)

for i , (a,b) in enumerate(E[:-1]):
    sa = sz[root(a)]; sb = sz[root(b)]
    if unite(a , b):
        sc = sz[root(a)]
        res += sc*(sc-1)//2 - sa*(sa-1)//2 - sb*(sb-1)//2
    ans[-2-i] = "%d\n" % (N*(N-1)//2 -res)
open(1, 'w').writelines(ans)