#include <bits/stdc++.h>
using namespace std;
using lint = long long int;
using pint = pair<int, int>;
using plint = pair<lint, lint>;
struct fast_ios {
  fast_ios() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    cout << fixed << setprecision(20);
  };
} fast_ios_;
template <typename T>
istream &operator>>(istream &is, vector<T> &vec) {
  for (auto &v : vec) is >> v;
  return is;
}
template <typename T>
ostream &operator<<(ostream &os, const vector<T> &vec) {
  os << "[";
  for (auto v : vec) os << v << ",";
  os << "]";
  return os;
}
template <typename T>
ostream &operator<<(ostream &os, const deque<T> &vec) {
  os << "deq[";
  for (auto v : vec) os << v << ",";
  os << "]";
  return os;
}
template <typename T>
ostream &operator<<(ostream &os, const set<T> &vec) {
  os << "{";
  for (auto v : vec) os << v << ",";
  os << "}";
  return os;
}
template <typename T>
ostream &operator<<(ostream &os, const unordered_set<T> &vec) {
  os << "{";
  for (auto v : vec) os << v << ",";
  os << "}";
  return os;
}
template <typename T>
ostream &operator<<(ostream &os, const multiset<T> &vec) {
  os << "{";
  for (auto v : vec) os << v << ",";
  os << "}";
  return os;
}
template <typename T>
ostream &operator<<(ostream &os, const unordered_multiset<T> &vec) {
  os << "{";
  for (auto v : vec) os << v << ",";
  os << "}";
  return os;
}
template <typename T1, typename T2>
ostream &operator<<(ostream &os, const pair<T1, T2> &pa) {
  os << "(" << pa.first << "," << pa.second << ")";
  return os;
}
template <typename TK, typename TV>
ostream &operator<<(ostream &os, const map<TK, TV> &mp) {
  os << "{";
  for (auto v : mp) os << v.first << "=>" << v.second << ",";
  os << "}";
  return os;
}
template <typename TK, typename TV>
ostream &operator<<(ostream &os, const unordered_map<TK, TV> &mp) {
  os << "{";
  for (auto v : mp) os << v.first << "=>" << v.second << ",";
  os << "}";
  return os;
}
template <typename T>
void ndarray(vector<T> &vec, int len) {
  vec.resize(len);
}
template <typename T, typename... Args>
void ndarray(vector<T> &vec, int len, Args... args) {
  vec.resize(len);
  for (auto &v : vec) ndarray(v, args...);
}
template <typename T>
void Max(T &m, const T q) {
  if (m < q) m = q;
}
template <typename T>
void Min(T &m, const T q) {
  if (m > q) m = q;
}
template <typename T1, typename T2>
pair<T1, T2> operator+(pair<T1, T2> &l, pair<T1, T2> &r) {
  return make_pair(l.first + r.first, l.second + r.second);
}
template <typename T1, typename T2>
pair<T1, T2> operator-(pair<T1, T2> &l, pair<T1, T2> &r) {
  return make_pair(l.first - r.first, l.second - r.second);
}
template <typename T>
struct WeightedUnionFind {
  vector<int> par, sz;
  vector<T> pot;
  WeightedUnionFind(int N) : par(N), sz(N, 1), pot(N) {
    for (int i = (0), i_end_ = (N); i < i_end_; i++) par[i] = i;
  }
  int find(int x) {
    if (par[x] != x) {
      int r = find(par[x]);
      pot[x] += pot[par[x]];
      par[x] = r;
    }
    return par[x];
  }
  bool unite(int s, int t, T diff) {
    diff += weight(s);
    diff -= weight(t);
    s = find(s), t = find(t);
    if (s == t) return diff == 0;
    if (sz[s] < sz[t])
      par[s] = t, sz[t] += sz[s], pot[s] = diff;
    else
      par[t] = s, sz[s] += sz[t], pot[t] = diff;
    return true;
  }
  T weight(int x) {
    find(x);
    return pot[x];
  }
  int size(int x) { return sz[find(x)]; }
  bool same(int x, int y) { return find(x) == find(y); }
};
void No() {
  puts("No");
  exit(0);
}
int main() {
  int N, M;
  cin >> N >> M;
  WeightedUnionFind<lint> wuf(N + 1);
  for (int _ = (0), __end_ = (M); _ < __end_; _++) {
    int L, R;
    lint D;
    cin >> L >> R >> D;
    if (!wuf.unite(L, R, D)) No();
  }
  puts("Yes");
}
