unittest
{
	assert( [ "4 5", "1 2", "3 4", "1 3", "2 3", "1 4" ].parse.expand.solve == "0\n0\n4\n5\n6" );
	assert( [ "6 5", "2 3", "1 2", "5 6", "3 4", "4 5" ].parse.expand.solve == "8\n9\n12\n14\n15" );
	assert( [ "2 1", "1 2" ].parse.expand.solve == "1" );
}



import std.conv;
import std.range;
import std.stdio;
import std.typecons;

void main()
{
	stdin.byLineCopy.parse.expand.solve.writeln;
}

auto parse( Range )( Range input )
if( isInputRange!Range && is( ElementType!Range == string ) )
{
	auto nm = input.front.split.to!( long[] );
	auto abs = new long[][]( nm[ 1 ], 2 );
	foreach( ref ab; abs )
	{
		input.popFront;
		ab = input.front.split.to!( long[] );
	}
	return tuple( nm[ 0 ], abs );
}

auto solve( long n, long[][] abs )
{
	// 不便さの最大値を計算
	auto f = n * ( n - 1 ) / 2;		// nC2をそのまま計算するとオーバーフローするので式変形した結果が左のやつ
	
	// 橋が崩落するときの不便さの増加を逆順で考えて、橋を架けるときの不便さの減少として計算
	auto anss = new long[]( abs.length );
	auto uft = UnionFind( n );
	foreach_reverse( ref ans, ab; lockstep( anss, abs ) )
	{
		ans = f;
		
		auto a = ab[ 0 ] - 1;
		auto b = ab[ 1 ] - 1;
		if( uft.same( a, b ) ) continue;
		
		// 橋を架けて不便さを減らす
		auto ac = uft.count( a );
		auto bc = uft.count( b );
		uft.unite( a, b );
		f -= ac * bc;
	}
	return anss.to!( string[] ).join( "\n" );
}

// UnionFind木
struct UnionFind
{
	private long[] ps;
	
	public this( long n )
	{
		ps = new long[]( n );
		ps[] = -1;		// マイナスは根であり、その木に属するノードの数でもある
	}
	
	public long root( long x )
	{
		return ( ps[ x ] < 0 ) ? x : ( ps[ x ] = root( ps[ x ] ) );
	}
	
	public bool same( long x, long y )
	{
		return root( x ) == root( y );
	}
	
	public void unite( long x, long y )
	{
		x = root( x );
		y = root( y );
		if( x == y ) return;
		if( ps[ x ] < ps[ y ] ){ ps[ x ] += ps[ y ]; ps[ y ] = x; }
		else                   { ps[ y ] += ps[ x ]; ps[ x ] = y; }
	}
	
	public long count( long x )
	{
		return -ps[ root( x ) ];
	}
}
