fn getline() -> String {
    let mut res = String::new();
    std::io::stdin().read_line(&mut res).ok();
    res
}

#[allow(unused_macros)]
macro_rules! readl {
    ($t: ty) => {
        {
            let s = getline();
            s.trim().parse::<$t>().unwrap()
        }
    };
    ($( $t: ty),+ ) => {
        {
            let s = getline();
            let mut iter = s.trim().split(' ');
            ($(iter.next().unwrap().parse::<$t>().unwrap(),)*)
        }
    };
}

#[allow(unused_macros)]
macro_rules! readlvec {
    ($t: ty) => {
        {
            let s = getline();
            let iter = s.trim().split(' ');
            iter.map(|x| x.parse().unwrap()).collect::<Vec<$t>>()
        }
    }
}

#[allow(unused_macros)]
macro_rules! debug {
    ($x: expr) => {
        println!("{}: {:?}", stringify!($x), $x)
    }
}

fn printiter<'a, T>(v: &'a T)
where
    &'a T: std::iter::IntoIterator,
    <&'a T as std::iter::IntoIterator>::Item: std::fmt::Display,
{
    for (i, e) in v.into_iter().enumerate() {
        if i != 0 {
            print!(" ");
        }
        print!("{}", e);
    }
    println!("");
}

struct UnionFind {
    par: Vec<usize>,
    rank: Vec<usize>,
    pot: Vec<i64>,
}

impl UnionFind {
    fn new(n: usize) -> UnionFind {
        let mut vec = vec![0;n];
        for i in 0..n {
            vec[i] = i;
        }
        UnionFind {
            par : vec,
            rank : vec![0;n],
            pot : vec![0; n],
        }
    }

    fn find(&mut self, x: usize) -> (usize, i64) {
        if x == self.par[x] {
            (x, 0)
        }else{
            let par = self.par[x];
            let dis = self.pot[x];
            let (ind, val) = self.find(par);
            self.par[x] = ind;
            self.pot[x] = val+dis;
            (ind, val+dis)
        }
    }

    fn same(&mut self, a: usize, b: usize) -> bool {
        self.find(a) == self.find(b)
    }

    fn unite(&mut self, a: usize, b: usize, c: i64) -> bool {
        let (apar, apot) = self.find(a);
        let (bpar, bpot) = self.find(b);
        if apar == bpar {
            apot-bpot == c
        } else {
            if self.rank[apar] > self.rank[bpar] {
                self.par[bpar] = apar;
                self.pot[bpar] = -c;
            } else {
                self.par[apar] = bpar;
                self.pot[apar] = c;
                if self.rank[apar] == self.rank[bpar] {
                    self.rank[bpar] += 1;
                }
            }
            true
        }
    }
}



#[allow(unused_imports)]
use std::cmp::{min, max};
#[allow(unused_imports)]
use std::collections::{BinaryHeap, BTreeMap, HashMap, HashSet};
#[allow(unused_imports)]
use std::collections::btree_map::Entry::{Occupied, Vacant};

fn main() {
    use std::io::Write;
    let out = std::io::stdout();
    let mut out = std::io::BufWriter::new(out.lock());
    macro_rules! print { ($($arg:tt)*) => (write!(out, $($arg)*).unwrap()); }
    macro_rules! println { () => (writeln!(out).unwrap()); ($($arg:tt)*) => (writeln!(out, $($arg)*).unwrap()); }
    let (n, m) = readl!(usize, usize);
    let mut uf = UnionFind::new(n);
    for _ in 0..m {
        let (l, r, d) = readl!(usize, usize, i64);
        if !uf.unite(l-1, r-1, d) {
            println!("No");
            return;
        }
    }
    println!("Yes");
}
