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



using namespace std;



#define fst(t) std::get<0>(t)

#define snd(t) std::get<1>(t)

#define thd(t) std::get<2>(t)

#define unless(p) if(!(p))

#define until(p) while(!(p))



using ll = long long;

using P = std::tuple<int,int>;



const int dx[8] = {-1, 1, 0, 0, -1, -1, 1, 1}, dy[8] = {0, 0, -1, 1, -1, 1, -1, 1};



vector<P> G[100100];

int N, M;

vector<int> vs;

int cnt[100100];

bool used[100100];

ll pos[100100];



void addEdge(int u, int v, int c){

    G[u].emplace_back(v, c);

}



void dfs(int v){

    used[v] = true;



    for(P& e : G[v]){

        int w;

        tie(w, ignore) = e;



        if(!used[w]){dfs(w);}

    }



    vs.emplace_back(v);

}



void dfs2(int v){

    used[v] = true;



    for(P& e : G[v]){

        int w, c;

        tie(w, c) = e;



        if(!used[w]){

            pos[w] = pos[v] + c;

            dfs2(w);

        }

    }

}



int main(){

    std::cin.tie(nullptr);

    std::ios::sync_with_stdio(false);



    std::cin >> N >> M;



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

        int L, R, D;

        std::cin >> L >> R >> D;



        addEdge(L, R, D);

        ++cnt[R];

    }



    for(int i=1;i<=N;++i){

        if(!used[i]){dfs(i);}

    }    



    memset(used, 0, sizeof(used));

    

    for(int i=N-1;i>=0;--i){

        if(cnt[vs[i]] == 0){

            pos[vs[i]] = 0;

        }



        if(!used[vs[i]]){dfs2(vs[i]);}

    }



    for(int i=1;i<=N;++i){

        for(P& e : G[i]){

            int j, c;

            tie(j, c) = e;



            if(pos[j] - pos[i] != c){

                std::cout << "No" << std::endl;

                return 0;

            }

        }



        if(pos[i] > 1000000000ll){

            std::cout << "No" << std::endl;

            return 0;            

        }

    }



    std::cout << "Yes" << std::endl;

}


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

#include <experimental/optional>



#define fst(t) std::get<0>(t)

#define snd(t) std::get<1>(t)

#define thd(t) std::get<2>(t)

#define unless(p) if(!(p))

#define until(p) while(!(p))



using ll = std::int64_t;

using P = std::tuple<int,int>;



struct UnionFind{

    using W = int;



    std::vector<int> parent, rank;

    // weight[v]: v -> v が属する木の根へのパスの重み

    // 注意: 使う前に find(v) を実行する必要がある

    std::vector<W> weight;

    

    UnionFind(int n) : parent(n), rank(n, 0), weight(n, 0) {

        std::iota(parent.begin(), parent.end(), 0);

    }



    int find(int v){

        if(v == parent[v]){return v;}

        

        int r = find(parent[v]);

        weight[v] += weight[parent[v]];

        parent[v] = r;



        return r;

    }

    

    std::experimental::optional<W> pathWeight(int s, int t){

        if(find(s) != find(t)){

            return {};

        }



        return weight[s] - weight[t];

    }



    // s から t へ重み w の辺を付け加える

    // 戻り値: 辺を付け加えても整合性が保たれるか

    bool unite(int s, int t, W w){

        int r1 = find(s), r2 = find(t);



        if(r1 == r2){

            return weight[s] - weight[t] == w;

        }



        if(rank[r1] > rank[r2]){

            parent[r2] = r1;

            weight[r2] = -(w - (weight[s] - weight[t]));

        }else{

            parent[r1] = r2;

            weight[r1] = w - (weight[s] - weight[t]);

            if(rank[r1] == rank[r2]){rank[r2] += 1;}

        }



        return true;

    }

};



int N, M;



int main(){

    std::cin.tie(nullptr);

    std::ios::sync_with_stdio(false);



    std::cin >> N >> M;



    UnionFind uf(N + 1);

    bool can = true;

    

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

        int l, r, d;

        std::cin >> l >> r >> d;



        unless(uf.unite(l, r, d)){

            can = false;

            break;

        }

    }



    const std::string YESNO[] = {"No", "Yes"};

    std::cout << YESNO[can] << std::endl;

}