use std::io::*;
use std::str::FromStr;
use std::collections::HashMap;
use std::collections::BTreeMap;
use std::collections::BTreeSet;
use std::collections::HashSet;
use std::cmp;
use std::f64::consts;
use std::cmp::Ordering;
use std::collections::VecDeque; //push_back, pop_front

const MOD: i64 = 1000000007;

fn main() {
    let n: usize = read();
    let k: usize = read();
    let mut all: usize = n*(n-1)/2;

    let mut vec: Vec<(usize, usize)> = Vec::new();
    for i in 0..k {
        let mut a: usize = read();
        let mut b: usize = read();
        a-=1;
        b-=1;

        vec.push((a,b));
    }
    vec.reverse();

    let mut ans_vec: Vec<usize> = Vec::new();
    ans_vec.push(all);
    let mut uf = UnionFindSize::new(n);
    for i in 0..k {
//        println!("asize: {} bsize: {}", uf.size(vec[i].0), uf.size(vec[i].1));
        if uf.find(vec[i].0) != uf.find(vec[i].1) {
            let mut minus = (uf.size(vec[i].0) * uf.size(vec[i].1));
            if minus > all {
                minus = all.clone();
            }
            all-=minus;
        }
        
        uf.unite(vec[i].0, vec[i].1);
        ans_vec.push(all);
    }
    ans_vec.reverse();

    for i in 1..ans_vec.len() {
        println!("{}", ans_vec[i]);
    }
}

struct UnionFindSize {
    parent: Vec<isize>,
    size: Vec<usize>,
    height: Vec<u64>,
}

impl UnionFindSize {
    fn new(n: usize) -> UnionFindSize {
        UnionFindSize {
            parent: vec![-1; n],
            size: vec![1usize; n],
            height: vec![0u64; n],
        }
    }

    fn find(&self, index: usize) -> usize {
        let mut index = index;
        while self.parent[index] >= 0 {
            index = self.parent[index] as usize;
        }
        index
    }

    fn same(&self, x: usize, y: usize) -> bool {
        self.find(x) == self.find(y)
    }

    fn size(&self, x: usize) -> usize {
        self.size[self.find(x)]
    }

    fn unite(&mut self, index0: usize, index1: usize) -> bool {
        let a = self.find(index0);
        let b = self.find(index1);
        if a == b {
            false
        } else {
            if self.height[a] > self.height[b] {
                self.parent[b] = a as isize;
                self.size[a] += self.size[b];
            } else if self.height[a] < self.height[b] {
                self.parent[a] = b as isize;
                self.size[b] += self.size[a];
            } else {
                self.parent[b] = a as isize;
                self.size[a] += self.size[b];
                self.height[a] += 1;
            }
            true
        }
    }
}

struct UnionFind {
    par: Vec<usize>,
    rank: Vec<usize>,
}

impl UnionFind {
    fn new(n: usize) -> UnionFind {
        let mut vec = vec![0;n];
        for i in 0..n {
            vec[i] = i;
        }
        UnionFind {
            par : vec,
            rank : vec![0;n],
        }
    }

    fn find(&mut self, x: usize) -> usize {
        if x == self.par[x] {
            x
        }else{
            let par = self.par[x];
            let res = self.find(par);
            self.par[x] = res;
            res
        }
    }

    fn same(&mut self, a: usize, b: usize) -> bool {
        self.find(a) == self.find(b)
    }

    fn unite(&mut self, a: usize, b: usize){
        let apar = self.find(a);
        let bpar = self.find(b);
        if self.rank[apar] > self.rank[bpar] {
            self.par[bpar] = apar;
        }else{
            self.par[apar] = bpar;
            if self.rank[apar] == self.rank[bpar] {
                self.rank[bpar] += 1;
            }
        }
    }
}


//(フェルマーの定理)
// (ab / a / b) % MOD を計算したいので、
// (ab * calc(a, MOD-2, MOD) % MOD * calc(b, MOD-2, MOD) % MOD を計算する。
fn calc(a: i64, b: i64, p: i64) -> i64 {
    if b == 0 {
        return 1;
    }
    if b % 2 == 0 {
        let d = calc(a, b/2, p);
        return (d * d) % p;
    } else {
        return (a * calc(a, b-1, p)) % p;
    }
}

// 素因数分解
fn prime_factorization(number: u64) -> Vec<u64> {
    let mut result = number;
    for i in 2..((number as f64).sqrt() as u64)+1 {
        if number % i == 0 {
            result = i;
            break;
        }
    }

    if result == number {
        return vec![number];
    } else {
        let mut v1 = vec![result];
        let mut v2 = prime_factorization(number / result);

        v1.append(&mut v2);

        return v1;
    }
}

fn calc_distance(xa: f64, ya: f64, xb: f64, yb: f64)->f64 {
    (f64::powf(xb-xa, 2.0) + f64::powf(yb-ya, 2.0)).sqrt()
}

fn calc_triangle_area(x1: f64, y1: f64, x2: f64, y2: f64, x3: f64, y3: f64) -> f64 {
    1.0/2.0 * ((x1-x3)*(y2-y3)-(x2-x3)*(y1-y3)).abs()
}

fn read<T: FromStr>() -> T {
    let stdin = stdin();
    let stdin = stdin.lock();
    let token: String = stdin
        .bytes()
        .map(|c| c.expect("failed to read char") as char)
        .skip_while(|c| c.is_whitespace())
        .take_while(|c| !c.is_whitespace())
        .collect();
    token.parse().ok().expect("failed to parse token")
}

// 最大公約数
fn gcd(a: i64, b: i64) -> i64 {
    match b {
        0 => a,
        _ => gcd(b, a % b)
    }
}

// 最小公倍数
fn lcm(a: i64, b: i64) -> i64 {
    let g = gcd(a ,b);
    a / g * b
}

// 階乗-dp (overflow回避で1000000007の余りを使って計算)
// 普通の階乗はmに1を指定
fn kaijou_dp(n: i64) -> i64 {
    let mut dp: Vec<i64> = vec![0; n as usize+1];
    dp[0] = 1;
    dp[1] = 1;

    for i in 1..n {
        dp[i as usize+1] = dp[i as usize] % MOD * (i+1)  % MOD;
    }
    return dp[n as usize];
}

// 階乗
fn kaijou(n: i64)->i64 {
    if n == 1 {
        return n;
    }
    return n * kaijou(n-1);
}

// 順列全列挙
pub trait LexicalPermutation {
    /// Return `true` if the slice was permuted, `false` if it is already
    /// at the last ordered permutation.
    fn next_permutation(&mut self) -> bool;
}

impl<T> LexicalPermutation for [T] where T: Ord {
    /// Original author in Rust: Thomas Backman <serenity@exscape.org>
    /// let mut data = [1, 2, 3];
    //    let mut permutations = Vec::new();
    //
    //    loop {
    //        permutations.push(data.to_vec());
    //        if !data.next_permutation() {
    //            break;
    //        }
    //    }
    //
    //    println!("{:?}", permutations);
    //    // [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]
    fn next_permutation(&mut self) -> bool {
        // These cases only have 1 permutation each, so we can't do anything.
        if self.len() < 2 { return false; }

        // Step 1: Identify the longest, rightmost weakly decreasing part of the vector
        let mut i = self.len() - 1;
        while i > 0 && self[i-1] >= self[i] {
            i -= 1;
        }

        // If that is the entire vector, this is the last-ordered permutation.
        if i == 0 {
            return false;
        }

        // Step 2: Find the rightmost element larger than the pivot (i-1)
        let mut j = self.len() - 1;
        while j >= i && self[j] <= self[i-1]  {
            j -= 1;
        }

        // Step 3: Swap that element with the pivot
        self.swap(j, i-1);

        // Step 4: Reverse the (previously) weakly decreasing part
        self[i..].reverse();

        true
    }
}

//iより小さい数字の数 = lower_bound(&a, i) as i64;
//iより大きい数字の数 = n - upper_bound(&c, i) as i64;
fn lower_bound<T: Ord>(vec: &Vec<T>, x: T) -> usize {
    let (mut left, mut right): (i64, i64) = (-1, vec.len() as i64);

    while (right - left) > 1 {
        let mid = (right + left) / 2;

        if x <= vec[mid as usize] {
            right = mid;
        } else {
            left = mid;
        }
    }

    return right as usize;
}

fn upper_bound<T: Ord>(vec: &Vec<T>, x: T) -> usize {
    let (mut left, mut right): (i64, i64) = (-1, vec.len() as i64);

    while (right - left) > 1 {
        let mid = (right + left) / 2;

        if x < vec[mid as usize] {
            right = mid;
        } else {
            left = mid;
        }
    }

    return right as usize;
}



Measured memory_bytes: 11516