Problem:
Score : 400 points

Problem Statement

There are N people standing on the x-axis.
Let the coordinate of Person i be x_i.
For every i, x_i is an integer between 0 and 10^9 (inclusive).
It is possible that more than one person is standing at the same coordinate.

You will given M pieces of information regarding the positions of these people.
The i-th piece of information has the form (L_i, R_i, D_i).
This means that Person R_i is to the right of Person L_i by D_i units of distance, that is, x_{R_i} - x_{L_i} = D_i holds.

It turns out that some of these M pieces of information may be incorrect.
Determine if there exists a set of values (x_1, x_2, ..., x_N) that is consistent with the given pieces of information.

Constraints

1 \leq N \leq 100 000

0 \leq M \leq 200 000

1 \leq L_i, R_i \leq N (1 \leq i \leq M)

0 \leq D_i \leq 10 000 (1 \leq i \leq M)

L_i \neq R_i (1 \leq i \leq M)

If i \neq j, then (L_i, R_i) \neq (L_j, R_j) and (L_i, R_i) \neq (R_j, L_j).

D_i are integers.

Input

Input is given from Standard Input in the following format:

N M
L_1 R_1 D_1
L_2 R_2 D_2
:
L_M R_M D_M

Output

If there exists a set of values (x_1, x_2, ..., x_N) that is consistent with all given pieces of information, print Yes; if it does not exist, print No.

Sample Input 1

3 3
1 2 1
2 3 1
1 3 2

Sample Output 1

Yes

Some possible sets of values (x_1, x_2, x_3) are (0, 1, 2) and (101, 102, 103).

Sample Input 2

3 3
1 2 1
2 3 1
1 3 5

Sample Output 2

No

If the first two pieces of information are correct, x_3 - x_1 = 2 holds, which is contradictory to the last piece of information.

Sample Input 3

4 3
2 1 1
2 3 5
3 4 2

Sample Output 3

Yes

Sample Input 4

10 3
8 7 100
7 9 100
9 8 100

Sample Output 4

No

Sample Input 5

100 0

Sample Output 5

Yes

Solution in Rust (1.15.1) (judge verdict: Wrong Answer)

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!("");
}

mod algebra {
    use std::ops::{Add, Div, Mul, Neg, Sub};
    use std;

    pub trait Zero {
        fn Zero() -> Self;
    }

    pub trait Monoid: Add<Self, Output = Self> + Zero
    where
        Self: std::marker::Sized,
    {}

    pub trait Group: Monoid + Neg<Output = Self> + Sub<Self, Output = Self> {}

    pub trait Ring: Group + One + Mul<Self, Output = Self> {}

    pub trait One {
        fn One() -> Self;
    }

    pub trait Field: Ring + Div<Self, Output = Self> {}
    
    impl<T: Add<T, Output = T> + Zero> Monoid for T {}
    impl<T: Monoid + Neg<Output = T> + Sub<T, Output = T>> Group for T {}
    impl<T: Group + One + Mul<T, Output = T>> Ring for T {}
    impl<T: Ring + Div<T, Output = T>> Field for T {}
}

struct UnionFind<Cost> 
where
    Cost: Copy + algebra::Group + Eq
{
    par: Vec<usize>,
    rank: Vec<usize>,
    pot: Vec<Cost>,
}

impl<Cost> UnionFind<Cost>
where
    Cost: Copy + algebra::Group + Eq
{
    fn new(n: usize) -> Self {
        let mut vec = vec![0;n];
        for i in 0..n {
            vec[i] = i;
        }
        UnionFind {
            par : vec,
            rank : vec![0;n],
            pot : vec![Cost::Zero(); n],
        }
    }

    fn find(&mut self, x: usize) -> (usize, Cost) {
        if x == self.par[x] {
            (x, Cost::Zero())
        } 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: Cost) -> 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
        }
    }
}
impl algebra::Zero for i64 {
    fn Zero() -> Self { 0 }
}
impl algebra::Zero for i32 {
    fn Zero() -> Self { 0 }
}



#[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, i32);
        if !uf.unite(l-1, r-1, d) {
            println!("No");
            return;
        }
    }
    println!("Yes");
}