// Slower C++ solution:
#include <bits/stdc++.h>

using namespace std;

#define GET_MACRO(_1, _2, _3, NAME, ...) NAME

#define _repl(i,a,b) for(int i=(int)(a);i<(int)(b);i++)

#define _rep(i,n) _repl(i,0,n)

#define rep(...) GET_MACRO(__VA_ARGS__, _repl, _rep)(__VA_ARGS__)

#define mp(a,b) make_pair((a),(b))

#define pb(a) push_back((a))

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

#define uniq(x) sort(all(x)),(x).erase(unique(all(x)),end(x))

#define fi first

#define se second

#define dbg(...) _dbg(#__VA_ARGS__, __VA_ARGS__)

void _dbg(string){cout<<endl;}

template<class H,class... T> void _dbg(string s,H h,T... t){int l=s.find(',');cout<<s.substr(0,l)<<" = "<<h<<", ";_dbg(s.substr(l+1),t...);}

template<class T,class U> ostream& operator<<(ostream &o, const pair<T,U> &p){o<<"("<<p.fi<<","<<p.se<<")";return o;}

template<class T> ostream& operator<<(ostream &o, const vector<T> &v){o<<"[";for(T t:v){o<<t<<",";}o<<"]";return o;}



#define INF 1120000000



using P = pair<int,int>;

vector<P> g[100005];



int pos[100005];

bool visited[100005];



bool solve(int v, int from){

  visited[v] = true;

  for(auto p : g[v]) if(p.first != from){

    int to = p.first;

    int nc = pos[v] + p.second;

    if(visited[to]){

      if(nc != pos[to]) return false;

    }

    else {

      pos[to] = nc;

      bool r = solve(to, v);

      if(!r) return false;

    }

  }

  return true;

}



int main(){

  int n,m;

  cin>>n>>m;

  rep(i,m){

    int l,r,d;

    cin>>l>>r>>d;

    l--;r--;

    g[l].pb(mp(r, d));

    g[r].pb(mp(l, -d));

  }



  rep(i,n) if(!visited[i]){

    pos[i] = 0;

    if(!solve(i, -1)){

      cout << "No" << endl;

      return 0;

    }

  }



  cout << "Yes" << endl;



  return 0;

}


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

using namespace std;

#define GET_MACRO(_1, _2, _3, NAME, ...) NAME

#define _repl(i,a,b) for(int i=(int)(a);i<(int)(b);i++)

#define _rep(i,n) _repl(i,0,n)

#define rep(...) GET_MACRO(__VA_ARGS__, _repl, _rep)(__VA_ARGS__)

#define mp(a,b) make_pair((a),(b))

#define pb(a) push_back((a))

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

#define uniq(x) sort(all(x)),(x).erase(unique(all(x)),end(x))

#define fi first

#define se second

#define dbg(...) _dbg(#__VA_ARGS__, __VA_ARGS__)

void _dbg(string){cout<<endl;}

template<class H,class... T> void _dbg(string s,H h,T... t){int l=s.find(',');cout<<s.substr(0,l)<<" = "<<h<<", ";_dbg(s.substr(l+1),t...);}

template<class T,class U> ostream& operator<<(ostream &o, const pair<T,U> &p){o<<"("<<p.fi<<","<<p.se<<")";return o;}

template<class T> ostream& operator<<(ostream &o, const vector<T> &v){o<<"[";for(T t:v){o<<t<<",";}o<<"]";return o;}



#define INF 1120000000



template <class T>

class WeightedUnionFind {

  int n;

  vector<int> par,sz;

  vector<T> w;

  // weight[i] : iがpar[i]よりどれだけ重いか

public:

  WeightedUnionFind(){}

  WeightedUnionFind(int _n) : n(_n) {

    par.resize(n, -1);

    sz.resize(n, 1);

    w.resize(n, 0);

  }

  int find(int i){

    if(par[i] < 0){

      return i;

    } else {

      int p = find(par[i]);

      w[i] += w[par[i]];

      par[i] = p;

      return p;

    }

  }

  T weight(int x){

    find(x);

    return w[x];

  }

  bool same(int x, int y){

    return find(x) == find(y);

  }

  // weight[i] +w の位置に j を配置

  void unite(int i, int j, T nw){

    nw += weight(i) - weight(j);

    int x = find(i), y = find(j);

    if(x == y) return;

    if(sz[x] < sz[y]){

      swap(x, y);

      nw = -nw;

    }

    sz[x] += sz[y];

    par[y] = x;

    w[y] = nw;

  }

  // x からみた y の相対位置

  T diff(int x, int y){

    return weight(y) - weight(x);

  }

};



bool solve(){

  int n,m;

  cin>>n>>m;



  WeightedUnionFind<int> uf(n);

  rep(i,m){

    int l,r,d;

    cin>>l>>r>>d;

    l--;r--;

    if(uf.same(l,r)){

      if(uf.diff(l,r) != d) return false;

    } else {

      uf.unite(l,r,d);

    }

  }

  return true;

}



int main(){

  cout << (solve() ? "Yes" : "No") << endl;



  return 0;

}