// Slower C++ solution:
#include <iostream>

#include <cstdio>

#include <cstdlib>

#include <cstring>

#include <cmath>

#include <map>

#include <set>

#include <vector>

#include <stack>

#include <queue>

#include <bitset>

#include <algorithm>

#include <numeric>

#include <functional>

using namespace std;



#define Rep(b, e, i) for(int i = b; i <= e; i++)

#define Repr(e, b, i) for(int i = e; i >= b; i--)

#define rep(n, i) Rep(0, n-1, i)

#define repr(n, i) Repr(n-1, 0, i)

#define all(v) (v).begin(), (v).end()

#define pb(v) push_back(v)

#define uniq(v) (v).erase(unique(all(v)),(v).end())

#define bitcnt(x) __builtin_popcount(x)



typedef long long ll;

typedef pair<ll, ll> llP;

typedef pair<int, int> intP;

typedef std::priority_queue<int> IntPrioQueue; //Z->A

typedef std::priority_queue<int, std::vector<int>, std::greater<int> > IntReversePrioQueue; //A->Z



//vector の中身を出力

template <class T>ostream &operator<<(ostream &o,const vector<T>&v)

{o<<"{";for(int i=0;i<(int)v.size();i++)o<<(i>0?", ":"")<<v[i];o<<"}";return o;}



const int MAX = 100010;



struct edge {int to; ll dis;};



vector <edge> adj[MAX];

ll dist[MAX];

bool vst[MAX];



bool dfs(int v, ll td) {

    vst[v] = true;

    dist[v] = td;

    bool f = true;

    for (edge e : adj[v]) {

        if (vst[e.to]) {

            f = f && td+e.dis == dist[e.to];

        }

        else f = f && dfs(e.to, td+e.dis);

    }

    return f;

}



void solve(void){

    int N, M;

    cin >> N >> M;

    rep(M, i) {

        int l, r; ll d;

        cin >> l >> r >> d;

        l--, r--;

        adj[l].pb((edge{r, d}));

        adj[r].pb((edge{l, -d}));

    }

    rep(N, i) {

        if (!vst[i] && !dfs(i, 0LL)) {

            cout << "No" << endl;

            return;

        }

    }

    cout << "Yes" << endl;

}



int main(void){

  solve();

  //cout << "yui(*-v・)yui" << endl;

  return 0;

}


// Faster C++ solution for the same problem (measured speedup: 3.5438844859783805x):
#include "bits/stdc++.h"



using namespace std;



//------------------------------- Libraries --------------------------------//



template <class T>

class WeightedUnionFind

{

    vector<int> par;

    vector<T> dist;



public:

    WeightedUnionFind(int n) : par(n, -1), dist(n, 0) {}



    int root(int x)

    {

        if (par[x] < 0)

            return x;

        else

        {

            //rootから降りて足していく

            int r = root(par[x]);

            //親までの累積和は計算し終わっている

            dist[x] += dist[par[x]];

            return par[x] = r;

        }

    }



    //d[y] = d[x] + w

    bool merge(int x, int y, T w)

    {

        //par[y] = x

        w += weight(x) - weight(y);

        x = root(x);

        y = root(y);

        if (x == y)

            return false;

        if (par[x] > par[y])

        {

            w = -w;

            swap(x, y);

        }

        par[x] += par[y];

        par[y] = x;

        dist[y] = w;

        return true;

    }



    T weight(int x)

    {

        root(x);

        return dist[x];

    }



    T dis(int x, int y) { return weight(y) - weight(x); }



    int size(int x) { return -par[root(x)]; }

};



//verified @ https://onlinejudge.u-aizu.ac.jp/solutions/problem/DSL_1_B/review/3922525/sifi_border/C++14



//------------------------------- Type Names -------------------------------//



using i64 = int_fast64_t;



using seika = string;

//akari : 1D, yukari : 2D, maki : 3D vector

template <class kizuna>

using akari = vector<kizuna>;

template <class yuzuki>

using yukari = akari<akari<yuzuki>>;

template <class tsurumaki>

using maki = akari<yukari<tsurumaki>>;

//akane : ascending order, aoi : decending order

template <class kotonoha>

using akane = priority_queue<kotonoha, akari<kotonoha>, greater<kotonoha>>;

template <class kotonoha>

using aoi = priority_queue<kotonoha>;



//------------------------------- Dubug Functions ---------------------------//

inline void print()

{

    cout << endl;

}

template <typename First, typename... Rest>

void print(const First &first, const Rest &... rest)

{

    cout << first << ' ';

    print(rest...);

}

//------------------------------- Solver ------------------------------------//



void solve()

{

    int n, m;

    cin >> n >> m;

    WeightedUnionFind<int> wuf(n);

    while (m--)

    {

        int l, r, d;

        cin >> l >> r >> d;

        l--, r--;

        if (!wuf.merge(l, r, d) && wuf.dis(l, r) != d)

        {

            cout << "No\n";

            return;

        }

    }

    cout << "Yes\n";

}



int main()

{

    solve();

    return 0;

}