unittest
{
	assert( [ "3 3", "1 2 1", "2 3 1", "1 3 2" ].parse.expand.solve == "Yes" );
	assert( [ "3 3", "1 2 1", "2 3 1", "1 3 5" ].parse.expand.solve == "No" );
	assert( [ "4 3", "2 1 1", "2 3 5", "3 4 2" ].parse.expand.solve == "Yes" );
	assert( [ "10 3", "8 7 100", "7 9 100", "9 8 100" ].parse.expand.solve == "No" );
	assert( [ "100 0" ].parse.expand.solve == "Yes" );
	
	assert( [ "5 4", "1 2 1", "3 4 2", "3 5 1", "4 5 1" ].parse.expand.solve == "No" );		// 追加ケース
}



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

auto solve( long n, long[][] lrds )
{
	// 辺配列を頂点基準に変換
	Tuple!( long, long )[][ long ] ess;
	foreach( lrd; lrds )
	{
		ess[ lrd[ 0 ] ] ~= tuple( lrd[ 1 ], +lrd[ 2 ] );
		ess[ lrd[ 1 ] ] ~= tuple( lrd[ 0 ], -lrd[ 2 ] );
	}
	
	// 島が複数ある可能性があるので全頂点対象でループ
	auto ps = new long[]( 1 + n );
	ps[] = long.max;
	DList!long queue;
	foreach( k; ess.keys )
	{
		if( ps[ k ] != long.max ) continue;
		
		// bfsで島一つ分の辺情報を整合性チェック
		ps[ k ] = 0;
		queue.insertBack( k );
		while( !queue.empty )
		{
			auto i = queue.front;
			queue.removeFront;
			foreach( e; ess[ i ] )
			{
				if( ps[ e[ 0 ] ] == long.max )
				{
					ps[ e[ 0 ] ] = ps[ i ] + e[ 1 ];
					queue.insertBack( e[ 0 ] );
				}
				else
				{
					if( ps[ e[ 0 ] ] != ps[ i ] + e[ 1 ] ) return "No";
				}
			}
		}
	}
	return "Yes";
}
