Problem:
Score : 400 points

Problem Statement

There are N people standing on the x-axis.
Let the coordinate of Person i be x_i.
For every i, x_i is an integer between 0 and 10^9 (inclusive).
It is possible that more than one person is standing at the same coordinate.

You will given M pieces of information regarding the positions of these people.
The i-th piece of information has the form (L_i, R_i, D_i).
This means that Person R_i is to the right of Person L_i by D_i units of distance, that is, x_{R_i} - x_{L_i} = D_i holds.

It turns out that some of these M pieces of information may be incorrect.
Determine if there exists a set of values (x_1, x_2, ..., x_N) that is consistent with the given pieces of information.

Constraints

1 \leq N \leq 100 000

0 \leq M \leq 200 000

1 \leq L_i, R_i \leq N (1 \leq i \leq M)

0 \leq D_i \leq 10 000 (1 \leq i \leq M)

L_i \neq R_i (1 \leq i \leq M)

If i \neq j, then (L_i, R_i) \neq (L_j, R_j) and (L_i, R_i) \neq (R_j, L_j).

D_i are integers.

Input

Input is given from Standard Input in the following format:

N M
L_1 R_1 D_1
L_2 R_2 D_2
:
L_M R_M D_M

Output

If there exists a set of values (x_1, x_2, ..., x_N) that is consistent with all given pieces of information, print Yes; if it does not exist, print No.

Sample Input 1

3 3
1 2 1
2 3 1
1 3 2

Sample Output 1

Yes

Some possible sets of values (x_1, x_2, x_3) are (0, 1, 2) and (101, 102, 103).

Sample Input 2

3 3
1 2 1
2 3 1
1 3 5

Sample Output 2

No

If the first two pieces of information are correct, x_3 - x_1 = 2 holds, which is contradictory to the last piece of information.

Sample Input 3

4 3
2 1 1
2 3 5
3 4 2

Sample Output 3

Yes

Sample Input 4

10 3
8 7 100
7 9 100
9 8 100

Sample Output 4

No

Sample Input 5

100 0

Sample Output 5

Yes

Solution in C++14 (GCC 5.4.1) (judge verdict: Wrong Answer)

//#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <iostream>
#include <queue>
#include <algorithm>
#include <sstream>
#include <vector>
#include <math.h>
#include <set>
#include <map>
#include <numeric>
#include <bitset>
#include <iomanip>
#include <cctype>
#include <cstdlib>      // srand,rand


using namespace std;
#define ll long long

#define modd 1000000007
#define INF 8223372036854775807ll


typedef pair<long long, long long> pl;



typedef string::const_iterator State;
class ParseError {};






/*

class UnionFind {
public:
	vector <ll> par;
	vector <ll> siz;

	// Constructor
	UnionFind(ll sz_) : par(sz_), siz(sz_, 1LL) {
		for (ll i = 0; i < sz_; ++i) par[i] = i;
	}
	void init(ll sz_) {
		par.resize(sz_);
		siz.assign(sz_, 1LL);  // resize だとなぜか初期化されなかった
		for (ll i = 0; i < sz_; ++i) par[i] = i; // 初期では親は自分自身
	}

	// Member Function
	// Find
	ll root(ll x) { // 根の検索
		while (par[x] != x) {
			x = par[x] = par[par[x]]; // x の親の親を x の親とする
		}
		return x;
	}

	// Union(Unite, Merge)
	bool merge(ll x, ll y) {
		x = root(x);
		y = root(y);
		if (x == y) return false;

		// merge technique（データ構造をマージするテク．小を大にくっつける）
		if (siz[x] < siz[y]) swap(x, y);
		siz[x] += siz[y];
		par[y] = x;
		return true;
	}



	bool issame(ll x, ll y) { // 連結判定
		return root(x) == root(y);
	}

	ll size(ll x) { // 素集合のサイズ
		return siz[root(x)];
	}


};*/

/*
struct SegmentTree {
private:
	ll n;
	vector<ll> node;

public:
	// 元配列 v をセグメント木で表現する
	SegmentTree(vector<ll> v) {
		// 最下段のノード数は元配列のサイズ以上になる最小の 2 冪 -> これを n とおく
		// セグメント木全体で必要なノード数は 2n-1 個である
		ll sz = v.size();
		n = 1; while (n < sz) n *= 2;
		node.resize(2 * n - 1, INF);

		// 最下段に値を入れたあとに、下の段から順番に値を入れる
		// 値を入れるには、自分の子の 2 値を参照すれば良い
		for (ll i = 0; i < sz; i++) node[i + n - 1] = v[i];
		for (ll i = n - 2; i >= 0; i--) node[i] = min(node[2 * i + 1], node[2 * i + 2]);
	}


	void update(ll x, ll val) {
		// 最下段のノードにアクセスする
		x += (n - 1);

		// 最下段のノードを更新したら、あとは親に上って更新していく
		node[x] = val;
		while (x > 0) {
			x = (x - 1) / 2;
			node[x] = min(node[2 * x + 1], node[2 * x + 2]);
		}
	}

};
*/





ll N, M, K, a, b, c, d, e, H, W, L, T;
ll x, y;
ll A[2000001] = {};
ll B[2000001] = {};
ll C[2000001] = {};
ll D[1000006] = {};
ll E[1000006] = {};
bool f;
string S[200000];
string SS;
set <long long>sll;
pl bufpl;
vector <long long>vl[300005];
vector <long long>vll;
vector <pl>vpl;
vector <string> vs;
set<ll> llset;
multiset<ll> llmset;
queue<ll> ql;
multiset<pl> plmset;


struct ST
{
	ll first;
	ll second;
	ll cost;

	bool operator<(const ST& another) const
	{
		return first < another.first;//比較
	};

	bool operator>(const ST& another) const
	{
		return first > another.first;//比較
	};

};
//queue<ST> qst;
priority_queue<ST, vector<ST>, greater<ST> > qst;
multiset<ST> stmset;
ST bufst;
ST stt[100000];


/*vector <ST> vst;
ST st[200005];
ST bufst;
bitset<5000> bits;*/


long long modinv(long long aa, long long mm) {
	long long bb = mm, uu = 1, vv = 0;
	while (bb) {
		long long tt = aa / bb;
		aa -= tt * bb; swap(aa, bb);
		uu -= tt * vv; swap(uu, vv);
	}
	uu %= mm;
	if (uu < 0) uu += mm;
	return uu;
}





ll zettai(ll aa) {

	if (aa < 0) {
		aa *= -1;
	}
	return aa;

}



class UnionFind
{

public:


	vector <ll>pairent;
	vector <ll>depth;
	vector <ll>size;

	vector <ll>iti;

	vector <ll>ko[200001];


	UnionFind(ll Amount) : pairent(Amount, 1), depth(Amount, 1), size(Amount, 1),iti(Amount,0) {

		for (ll i = 0; i < Amount; i++) {
			pairent[i] = i;
		}


	}


	ll FindPairent(ll x) {
		if (pairent[x] == x)return x;
		else return pairent[x] = FindPairent(pairent[x]);

	}


	ll Merge(ll x, ll y) {
		x = FindPairent(x);
		y = FindPairent(y);

		if (x != y) {
			if (depth[x] > depth[y]) {

				pairent[y] = pairent[x];
				
				for (int i = 0; i < ko[x].size(); i++) {
					ko[y].push_back(ko[x][i]);
				}

				ko[y].push_back(x);

				return size[x] += size[y];
			}
			else {
				pairent[x] = pairent[y];
				if (depth[x] == depth[y]) {
					depth[y] ++;
				}

				for (int i = 0; i < ko[y].size(); i++) {
					ko[x].push_back(ko[y][i]);
				}

				ko[x].push_back(y);

				return size[y] += size[x];
			}

		}
		else {
			return -1;
		}



	}



	bool IsSame(ll x, ll y) {
		if (FindPairent(x) == FindPairent(y))return true;
		else return false;
	}


	ll GetSize(ll x) {
		x = FindPairent(x);
		return size[x];
	}


};

class Nibutan {

public:
	ll hidari = 0;
	ll migi = 0;
	ll abc;
	vector<ll> contents;

	bool Jouken(int ser) {

		if (contents[ser] > abc) {
			return true;
		}
		else {
			return false;
		}

	}

	void Settings(int amount, ll terget) {
		migi = amount - 1;
		abc = terget;
	}


	ll tansaku() {

		hidari = -1;
		migi = contents.size();
		ll jud;

		while (hidari != migi - 1) {
			jud = hidari + migi;
			jud /= 2;
			if (Jouken(jud)) {
				migi = jud;
			}
			else {
				hidari = jud;
			}

		}

		return hidari;

	}


};





UnionFind uf(200001);

int main() {

	cin >> N>>M;




	for (int i = 0; i < N; i++) {

		D[i] = INF;


	}

	SS = "Yes";

	for (int i = 0; i < M; i++) {
		cin >> A[i] >> B[i] >> C[i];

		A[i]--;
		B[i]--;
		



		if (D[A[i]] == INF) {


			if (D[B[i]] == INF) {
				D[A[i]] = 0;
				D[B[i]] = C[i];
				uf.Merge(A[i], B[i]);
			}
			else {
				D[A[i]] = D[B[i]] - C[i];
				uf.Merge(A[i], B[i]);
			}

		}
		else {

			if (D[B[i]] == INF) {
				D[B[i]] = D[A[i]] + C[i];
				uf.Merge(A[i], B[i]);
			}
			else {

				if (uf.IsSame(A[i], B[i])) {
					if (D[A[i]] + C[i] != D[B[i]]) {
						SS = "No";
					}

				}
				else {

					a = D[B[i]] - (D[A[i]] + C[i]);
					D[B[i]] = D[A[i]] + C[i];

					for (int j = 0; j < uf.ko[uf.FindPairent(B[i])].size(); j++) {
						if (uf.IsSame(uf.ko[uf.FindPairent(B[i])][j], B[i])) {
							D[uf.ko[uf.FindPairent(B[i])][j]] -= a;
						}
					}

					uf.Merge(A[i], B[i]);

				}

			}
			
		}

	}

	cout << SS << endl;


	//cout << fixed << setprecision(10) << ansa << endl;
	return 0;




}