unittest
{
	assert( [ "3 1", "1 2 1" ].parse.expand.solve == 2 );
	assert( [ "6 5", "1 2 1", "2 3 2", "1 3 3", "4 5 4", "5 6 5" ].parse.expand.solve == 2 );
	assert( [ "100000 1", "1 100000 100" ].parse.expand.solve == 99999 );
}



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 xyzs = new long[ 3 ][ nm[ 1 ] ];
	foreach( ref xyz; xyzs )
	{
		input.popFront;
		xyz = input.front.split.to!( long[] );
		xyz[ 0 .. 2 ] -= 1;
	}
	return tuple( nm[ 0 ], xyzs );
}

auto solve( long n, long[ 3 ][] xyzs )
{
	auto utf = UnionFind( n );
	foreach( xyz; xyzs )
	{
		utf.unite( xyz[ 0 ], xyz[ 1 ] );
	}
	return utf.rootnum;
}

struct UnionFind
{
	private long   num;
	private long[] ps;
	
	public this( long n )
	{
		num = 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; }
		num--;
	}
	
	public long rootnum()
	{
		return num;
	}
}
