//g++ -std=c++14 test.cpp -o test.out
//問題URL
//https://atcoder.jp/contests/abc087/tasks/arc090_b

//重み付きUnion find
//参考：https://qiita.com/drken/items/cce6fc5c579051e64fab

#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <cassert>
#include <algorithm>
#include <functional>
#include <iostream>
#include <iomanip>
#include <vector>
#include <queue>
#include <map> 
#include <utility>
#include <limits.h>
#include <bitset>
#include <set>
using namespace std;
 
#define LL long long int
const LL INF = (LL)(1e18)+1;
const int INF_INT = 2147483647-1e6-1;
const LL mod = 1000000007ll;
const int mod_int = 1000000007;

struct UnionFind {
    vector<LL> parent; // 親ノード
    vector<LL> rank; // ランク
    vector<LL> diff_weight; //親ノードとの重みの差分

    UnionFind(LL n = 1) { init(n);}

    void init(LL n = 1) {
        parent.resize(n); rank.resize(n); diff_weight.resize(n);
        for (LL i = 0; i < n; ++i) parent[i] = i, rank[i] = 0, diff_weight[i]=0;
    }

    //重みつき Union-Find では、経路圧縮のついでに差分重み diff_weight も更新する
    LL root(LL x) {
        if (parent[x] == x) return x;
        else {
            LL r = root(parent[x]);
            diff_weight[x] += diff_weight[parent[x]]; // 累積和をとる
            return parent[x] = r;
        }
    }

    //点xと、その点の属する木の根　間の距離を返す
    LL weight_from_root(LL x) {
        root(x); // 経路圧縮
        return diff_weight[x];
    }

    //yの根っこから見た距離-xの根っこから見た距離を返す
    LL weight_diff(LL x,LL y) {
        return weight_from_root(y) - weight_from_root(x);
    }

    bool issame(LL x,LL y) {
        return root(x) == root(y);
    }

    bool merge(LL x,LL y) {
        x = root(x); y = root(y);
        if (x == y) return false;
        if (rank[x] < rank[y]) swap(x, y);
        if (rank[x] == rank[y]) rank[x]++;
        parent[y] = x;
        return true;
    }

    //xの属する木、yの属する木を統合 ただしx,y間の距離がwであるとする
    //x,yがすでに同じ木に属しているならfalseを返す
    //weight_from_root(y) - weight_from_root(x) = w となるように merge する
    bool merge(LL x,LL y,LL w) {
        // x と y それぞれについて、 root との重み差分を補正
        //merge(x, y) 操作では、元々の x と y との間に辺を繋ぐのではなく、
        //root(x) と root(y) の間をつなぐので、
        //つなぐべき辺の重みは w ではなく修正が必要になる。
        w += (weight_from_root(x) - weight_from_root(y));
        //xとyのrootを見る(xとyが既につながっていたらfalseを返す)
        x = root(x);
        y = root(y);
        if (x == y) return false;
        // rank[x] >= rank[y] となるように x と y を swap (それに合わせて w も符号反転する)
        if (rank[x] < rank[y]) swap(x, y), w = -w;    
        // y (のroot) を x (のroot) の下にくっつける 
        if (rank[x] == rank[y]) rank[x]++;
        parent[y] = x; 
        // x が y の親になるので、x と y の差分を diff_weight[y] に記録
        diff_weight[y] = w; 
        return true;
    }
};

LL N,M;
tuple<LL,LL,LL> LRD[200000];

string ans = "Yes";

void solve(){
    //例えばleft=4,right=3,distance=1と与えられたならば
    //点4と点3を同じ木に所属させ、点4と点3の、根からの距離を
    //union findに保存しておく
    UnionFind uf(N);
    for(int i=0;i<M;i++){
        //点leftより点rightが距離distanceだけ離れている、という情報
        LL left = get<0>(LRD[i]);
        LL right = get<1>(LRD[i]);
        LL distance = get<2>(LRD[i]);
        left--,right--;
        //left,rightが同じ木に属しているならば
        if(uf.issame(left,right)){
            //rightの根っこから見た距離-leftの根っこから見た距離を返す
            LL diff = uf.weight_diff(left,right);
            //left,right間の距離が与えられた距離と一致しなければ
            //矛盾すると分かる
            if(diff != distance){
                ans = "No";
                return;
            }
        }
        //left,rightが同じ木に属していないならば統合
        else uf.merge(left,right,distance);
    }
}

int main(){
    cin >> N >> M;
    for(int i=0;i<M;i++){
        LL l,r,d;cin >> l >> r >> d;
        LRD[i] = tuple<LL,LL,LL>(l,r,d);
    }
 
    solve();

    cout << ans << endl;
    return 0;
}

Measured memory_bytes: 7296