import std.stdio, std.string, std.conv;
import std.range, std.algorithm, std.array;
import std.typecons : Tuple;

alias Edge = Tuple!(int, "to", int, "cost");

void main() {
    int n,m;
    scan(n,m);

    auto adj = new Edge[][](n, 0);

    foreach (i ; 0 .. m) {
        int a, b, c;
        scan(a, b, c);
        a--, b--;
        adj[a] ~= Edge(b, c);
    }

    auto mark = new int[](n);
    auto top = new int[](0);
    bool heiro = 0;

    void visit(int i) {
        if (mark[i] == 2) {
            return;
        }
        else if (mark[i] == 1) {
            heiro = 1;
            return;
        }
        else {
            mark[i] = 1;

            foreach (e ; adj[i]) {
                visit(e.to);
            }

            mark[i] = 2;
            top ~= i;

            return;
        }
    }

    foreach (i ; 0 .. n) {
        if (!mark[i]) {
            visit(i);
        }
    }

    if (heiro) {
        writeln("No");
        return;
    }

    top.reverse();

    debug {
        writeln(top);
    }

    immutable inf = 10L^^17;

    auto d = new long[](n);
    d[] = inf;
    d[top.front] = 0;

    foreach (i ; top) {
        foreach (e ; adj[i]) {
            if (d[e.to] == inf) {
                d[e.to] = d[i] + e.cost;
            }
            else {
                if (d[e.to] != d[i] + e.cost) {
                    writeln("No");
                    return;
                }
            }
        }
    }

    writeln("Yes");
}




void scan(T...)(ref T args) {
    import std.stdio : readln;
    import std.algorithm : splitter;
    import std.conv : to;
    import std.range.primitives;

    auto line = readln().splitter();
    foreach (ref arg; args) {
        arg = line.front.to!(typeof(arg));
        line.popFront();
    }
    assert(line.empty);
}



void fillAll(R, T)(ref R arr, T value) {
    static if (is(typeof(arr[] = value))) {
        arr[] = value;
    }
    else {
        foreach (ref e; arr) {
            fillAll(e, value);
        }
    }
}
