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

#define int long long

#define uint unsigned int

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

#define repr(i, a, b) for (int i = a; i >= b; i--)

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

#define sz(a) (a).size()

#define pb(a) push_back(a)

#define eb(...) emplace_back(__VA_ARGS__)

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

#define mt(...) make_tuple(__VA_ARGS__)

using namespace std;

using pii = pair<int, int>;

constexpr int MOD = 1000000007;

constexpr int INF = 1LL << 30;

constexpr double EPS = 1e-10;



struct edge {

  int t, c;

  edge() {}

  edge(int t, int c) : t(t), c(c) {}

};



int N, M;

vector<edge> g[100010];

int dis[100010];



bool dfs(int v, int x) {

  dis[v] = x;

  for (edge e : g[v]) {

    int nv = e.t;

    if (dis[nv] == INF) {

      if (dfs(nv, x+e.c)) continue;

      else return false;

    } else {

      if (dis[nv] == x+e.c) continue;

      return false;

    }

  }

  return true;

}



signed main() {

  cin.tie(0);

  ios_base::sync_with_stdio(false);

  cout << fixed << setprecision(10);

  

  cin >> N >> M;

  rep(i, 0, M) {

    int L, R, D;

    cin >> L >> R >> D;

    L--; R--;

    g[L].eb(R, D);

    g[R].eb(L, -D);

  }



  fill(dis, dis+N, INF);

  rep(i, 0, N) {

    if (dis[i] == INF) {

      if (!dfs(i, 0)) {

        cout << "No" << endl;

        return 0;

      }

    }

  }

  cout << "Yes" << endl;



  return 0;

}

// Optimized solution to the same problem (faster):
#include <bits/stdc++.h>

#define int long long

#define pii pair<int,int>

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

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

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

#define ZERO(a) memset(a,0,sizeof(a))

#define MINUS(a) memset(a,0xff,sizeof(a))

#define MINF(a) memset(a,0x3f,sizeof(a))

#define POW(n) (1LL<<(n))

#define IN(i,a,b) (a <= i && i <= b)

using namespace std;

template <typename T> inline bool CHMIN(T& a,T b) { if(a>b) { a=b; return 1; } return 0; }

template <typename T> inline bool CHMAX(T& a,T b) { if(a<b) { a=b; return 1; } return 0; }

template <typename T> inline void SORT(T& a) { sort(ALL(a)); }

template <typename T> inline void REV(T& a) { reverse(ALL(a)); }

template <typename T> inline void UNI(T& a) { sort(ALL(a)); a.erase(unique(ALL(a)),a.end()); }

const int MOD = 1000000007;

const int INF = 0x3f3f3f3f3f3f3f3f;

const double EPS = 1e-10;

/* ---------------------------------------------------------------------------------------------------- */



template <typename T>

struct WUF {

  int n, c;

  vector<int> p;

  vector<T> dw;

  WUF() {}

  WUF(int n) : n(n), c(n), p(n, -1), dw(n) {}

  int find(int x) {

    if (p[x] < 0) return x;

    int r = find(p[x]);

    dw[x] += dw[p[x]];

    return p[x] = r;

  }

  // x から見た y の重みを w に設定する

  int unite(int x, int y, T w) {

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

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

    if (x == y) return w == 0;

    if (p[x] > p[y]) swap(x,y), w = -w;

    p[x] += p[y];

    p[y] = x;

    dw[y] = w;

    c--;

    return 1;

  }

  // 根から見た x の重み

  T weight(int x) {

    find(x);

    return dw[x];

  }

  // x から見た y の重み

  T diff(int x, int y) {

    assert(same(x, y));

    return weight(y) - weight(x);

  }

  bool same(int x, int y) {

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

  }

  // x を含む集合のサイズ

  int size(int x) {

    return -p[find(x)];

  }

  // 連結成分の個数

  int count() {

    return c;

  }

};



signed main() {

  cin.tie(0);

  ios_base::sync_with_stdio(false);

  cout << fixed << setprecision(10);



  int N,M;

  cin >> N >> M;

  WUF<int> wuf(N);

  bool yes = true;

  REP(i,M) {

    int L,R,D;

    cin >> L >> R >> D;

    L--; R--;

    yes &= wuf.unite(L,R,D);

  }

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



  return 0;

}

// Fastest known solution for this problem: