// Original C++ solution (slower):
#include<bits/stdc++.h>



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

#define REP(i,n)   FOR(i,0,n)

#define ll long long

#define PINT pair<int,int>

const int INF = 1e9 + 7;



using namespace std;



#define MAX_V 100001

vector<PINT> p[MAX_V];

int n,ddfs,sum;

bool cdfs[MAX_V];

ll dis[MAX_V];



bool dfs(int from){

    REP(i,p[from].size()){

        int to = p[from][i].first;

        if(!cdfs[to]){

            cdfs[to] = true;

            dis[to] = dis[from] + p[from][i].second;

            if(!dfs(to)) return false;

        }else if(dis[to]!=dis[from]+p[from][i].second) return false;

    }

    return true;

}



#define MAX_N 100001

int par[MAX_N]; //親

int rnk[MAX_N]; //木の深さ



//n要素で初期化

void init(int n){

  REP(i,n){

    par[i] = i;

    rnk[i] = 0;

  }

}



//木の根を求める

int find(int x){

  if(par[x] == x){

    return x;

  }else{

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

  }

}



//xとyの属する集合を併合

void unite(int x, int y){

  x = find(x);

  y = find(y);

  if(x == y) return;



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

    par[x] = y;

  }else{

    par[y] = x;

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

  }

}



//xとyが同じ集合に属するか

bool same(int x, int y){

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

}



int main(){

    cin.tie(0), ios::sync_with_stdio(false);

    int m;

    bool ans=true;

    cin >> n >> m;

    vector<bool> used(n+1,false);

    init(n+1);

    REP(i,m){

        int l,r,d;

        cin >> l >> r >> d;

        p[l].push_back({r,d});

        p[r].push_back({l,-d});

        unite(l,r);

    }



    set<int> abc;

    FOR(i,1,n+1) abc.insert(find(i));

    FOR(i,1,n+1) cdfs[i] = false;

    for(auto itr=abc.begin();itr!=abc.end();++itr){

        dis[*itr] = 0; cdfs[*itr] = true;

        ans = dfs(*itr);

        if(!ans) break;

    }



    if(ans) cout << "Yes" << endl;

    else cout << "No" << endl;

    return 0;

}


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

using namespace std;



typedef int VAL;

const int MAX_UF = 110000;

const VAL SUM_UNITY = 0;



struct UnionFind {

  int par[MAX_UF];

  int rank[MAX_UF];

  VAL diff_weight[MAX_UF];



  UnionFind(int n = 0) {

    for (int i = 0; i < n; ++i) par[i] = i, rank[i] = 0, diff_weight[i] = SUM_UNITY;

  }



  void init(int n) {

    for (int i = 0; i < n; ++i) par[i] = i, rank[i] = 0, diff_weight[i] = SUM_UNITY;

  }



  int root(int x) {

    if (par[x] == x) {

      return x;

    }

    else {

      int r = root(par[x]);

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

      return par[x] = r;

    }

  }



  VAL weight(int x) {

    root(x);

    return diff_weight[x];

  }



  bool issame(int x, int y) {

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

  }



  bool merge(int x, int y, VAL w) {

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

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

    if (x == y) return 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;

  }



  VAL diff(int x, int y) {

    return weight(y) - weight(x);

  }



} uf;



int main() {

  cin.tie(0), ios::sync_with_stdio(false);

  int N, M;

  cin >> N >> M;

  uf.init(N);

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

    int l, r, d;

    cin >> l >> r >> d;

    --l, --r;

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

      int diff = uf.diff(l, r);

      if (diff != d) {

        cout << "No" << endl;

        return 0;

      }

    }

    else {

      uf.merge(l, r, d);

    }

  }

  cout << "Yes" << endl;

  return 0;

}


// Fastest known solution for this problem: