#include <bits/stdc++.h>
using namespace std;
///////////////////////////////////////////
const long long int INF = 1LL<<60;
const long long int Mod = 1000000007;
using ll = long long int; using ci = const int;
using vi = vector<int>;  using Vi = vector<long long int>;
using P = pair<int, int>;  using PLL = pair<ll, ll>;
using matrix = vector<vector<ll>>;
#define pb(x) push_back(x)
#define mp(x,y) make_pair(x,y)
#define all(x) (x).begin(),(x).end()
#define rp(i,N) for(ll i = 0; i < (ll)N; i++)
#define repi(i,a,b) for(ll i = ll(a); i < ll(b); ++i)
template<class T>bool chmax(T &former, const T &b) { if (former<b) { former=b; return true; } return false; }
template<class T>bool chmin(T &former, const T &b) { if (b<former) { former=b; return true; } return false; }
template<class T>T sqar(T x){ return x*x; }//sqrt(x)は平方根;
#define Sort(v) std::sort(v.begin(), v.end(), std::greater<decltype(v[0])>()) //降順でVをソート
#define p_queue(v) priority_queue<v, vector<v>, greater<v> >
template<class T> inline void princ(T x){cout<<x<<" ";}; 
template<class T> inline void print(T x){cout<<x<<"\n";};
template<class T> inline void Yes(T condition){ if(condition) cout << "Yes" << endl; else cout << "No" << endl; }
template<class T> inline void YES(T condition){ if(condition) cout << "YES" << endl; else cout << "NO" << endl; }
///////////////////////////////////////////////////////////////////////////////////
struct edge{/*重み付き,有向*/
    ll to , cost;/*toは接続先*/
    edge(ll to_ ,ll cost_){ to=to_; cost=cost_; };/*全てのcost=1 <=> 無向グラフ*/
};
bool operator<(edge e1 ,edge e2){return e1.cost < e2.cost; }
bool operator>(edge e1 ,edge e2){return e1.cost > e2.cost; }
using graph =  vector< vector<edge> >;

Vi indegree;//頂点の入次数を管理.
queue<ll> _q;//入次数0の頂点集合
void _set_indegree(graph _g){//入次数を計算し,配列に持っておく.
    indegree.resize(_g.size());
    rp(i,_g.size()){
        indegree[i] = _g[i].size();
        if(indegree[i]==0){
            _q.push(i);
        }
    }
}

Vi topologicalsort(graph _g){//有向非順回グラフについて,根本からソートされた配列を返す
    Vi sorted;
    graph _g2(_g.size());
    rp(i,_g.size()){
        rp(j,_g[i].size()){
            _g2[_g[i][j].to].pb(_g[i][j]);
        }
    }
    _set_indegree(_g2);
    while(!_q.empty()){
        ll now = _q.front();
        _q.pop();
        sorted.pb(now);
        rp(i,_g[now].size()){//入次数が0のものを取り出して,ソート済み配列に突っ込んで,それに隣接する頂点の入次数を更新
            ll id = _g[now][i].to;
            indegree[id]--;
            if(indegree[id]==0){
                _q.push(id);
            }
        }
    }
    return sorted;
}
void solve(){
    ll n,m;
    cin >> n >> m;
    graph G(n+1);
    rp(i,m){
        ll l,r,d;
        cin >> l >> r >> d;
        edge e(r,d);
        G[l].pb(e);
    }
    
    Vi sorted;
    sorted=topologicalsort(G);
    /*for(auto a:sorted){
        princ(a);
    }
    cout << endl;*/
    if((int)sorted.size()!=n+1){
        print("No");
        return;
    }
    Vi dist(n+1,-1);
    dist[sorted[1]]=0;
    repi(i,1,n+1){
        if(dist[sorted[i]]==-1&&(int)G[sorted[i]].size()==1) continue;
        for(auto e:G[sorted[i]]){
            if(dist[e.to]!=-1&&dist[e.to]!=dist[sorted[i]]+e.cost){
                print("No");
                return;
            }
            dist[e.to]=dist[sorted[i]]+e.cost;
        }
    }
    print("Yes");
    return;
}
int main(){
    cin.tie(0);ios::sync_with_stdio(false);
    std::cout<<std::fixed<<std::setprecision(30);
    solve();
    return 0;
}

Measured memory_bytes: 32428