// Original C++ solution (slower):
#include <iostream>

#include <string>

#include <algorithm>

#include <iomanip>

#include <vector>

#include <map>

#include <cmath>

#include <queue>

#include <utility>

#include <functional>

#include <deque>

#include <cctype>

#include <stack>

#include <bitset>



using ll = long long;

typedef unsigned long long ull;

typedef std::pair<ll, ll>P;



ll min(ll a, ll b) {

	if (a >= b)return b;

	else return a;

}

ll max(ll a, ll b) {

	if (a >= b)return a;

	else return b;

}

ll gcd(ll a, ll b) {

	if (b == 0) return a;

	return gcd(b, a%b);

}

ll lcm(ll a, ll b) {

	ll g = gcd(a, b);

	return a / g * b;

}



const ll Z = 1000000007;

const ll INF = 1 << 30;

const ll INF2 = 9000000000000000000LL;

bool fl = true;

int dx[4] = { 1,0,-1,0 }, dy[4] = { 0,1,0,-1 };

std::string abc = "abcdefghijklmnopqrstuvwxyz";

struct edge { ll to, cost; };

double pai = 3.141592653589793;

std::vector<P>v[100010];

int d[100010];

bool flag[100010];



void dfs(int node, int t) {

	if (flag[node]) {

		if (d[node] != t) {

			std::cout << "No" << std::endl;

			exit(0);

		}

		return;

	}

	flag[node] = true;

	d[node] = t;

	for (int i = 0;i < v[node].size();i++)dfs(v[node][i].first, t + v[node][i].second);

}



int main() {

	int n, m;

	std::cin >> n >> m;

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

		ll l, r, c;

		std::cin >> l >> r >> c;

		v[l].push_back(P(r,c));

		v[r].push_back(P(l,-c));

	}

	for (int i = 1;i <= n;i++) {

		if (!flag[i])dfs(i, 0);

	}

	std::cout << "Yes" << std::endl;

}

// Optimized solution to the same problem (faster):
#include <iostream>

#include <algorithm>

#include <cmath>

#include <map>

#include <vector>

#include <queue>

#include <functional>

#include <string>

#include <stack>

#include <set>

#include <sstream>

#include <iomanip>

#include <limits>

#include <cstring>





using namespace std;

using ll = long long;

typedef unsigned long long ull;

typedef pair<ll, ll> P;

typedef pair<double, double >pd;

typedef pair<string, string> sP;

typedef pair<ll, P> PP;





const ll mod = 1e4;

const ll MOD = 1e9 + 7;

const ll MOD2 = 998244353;

const ll INF = 1 << 30;

const ll INF2 = 9e18;

const double INF3 = 9e14;

const double eps = 1e-10;

const double PI = 3.14159265358979323846;

const int dx[4] = { 1,0,-1,0 }, dy[4] = { 0,1,0,-1 };

const int tx[8] = { -1,0,1,-1,1,-1,0,1 }, ty[8] = { -1,-1,-1,0,0,1,1,1 };





#define ALL(x) (x).begin(),(x).end()

#define ALLR(x) (x).rbegin(),(x).rend()

#define pb push_back

#define eb emplace_back

#define fr first

#define sc second



int N, M, L, R, D;



struct weight_Union_Find_Tree {

private:

	vector<ll>par;

	vector<ll>Rank;

	vector<ll>diff_weight;

public:

	void init(int n) {//初期化

		n += 10;

		par.resize(n);

		Rank.resize(n);

		diff_weight.resize(n);

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

			par[i] = i;

			Rank[i] = 0;

			diff_weight[i] = 0;

		}

		return;

	}



	ll root(int x) {//経路圧縮＋重み差分更新

		if (par[x] == x) return x;

		ll r = root(par[x]);

		diff_weight[x] += diff_weight[par[x]];

		return par[x] = r;

	}



	ll weight(int x) {//重みの取得

		root(x);

		return diff_weight[x];

	}



	bool same(int x, int y) {//根が同じか判定

		if (root(x) == root(y))return true;

		return false;

	}



	bool merge(int x, int y, ll w) {//併合したい頂点x,yと、その間の重みw

		w += weight(x);

		w -= weight(y);

		x = root(x), y = root(y);

		if (x==y)return false;//すでに併合されているならfalse

		if (Rank[x] < Rank[y])swap(x, y), w = -w;

		if (Rank[x] == Rank[y])Rank[x]++;

		par[y] = x;

		diff_weight[y] = w;

		return true;

	}



	ll diff(int x, int y) {//頂点間の重みの差分を返す

		return weight(y) - weight(x);

	}

};





int main() {	

	cin >> N >> M;

	weight_Union_Find_Tree uf;

	uf.init(N);

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

		cin >> L >> R >> D;

		if (!uf.merge(L, R, D) && uf.diff(L, R) != D) {

			cout << "No" << endl;

			return 0;

		}

	}

	cout << "Yes" << endl;

	return 0;

}




// Fastest known solution for this problem: