// Slower C++ solution:
#include <cstdio>

#include <iostream>

#include <algorithm>

#include <cstring>

#include <queue>



using namespace std;



struct edge

{

    int v,w,nxt;

}e[400050];



int head[100050],ecnt;



void adde(int u,int v,int w)

{

    e[++ecnt].v = v;

    e[ecnt].w = w;

    e[ecnt].nxt = head[u];

    head[u] = ecnt;

}



int N,M;

int l[200050],r[200050];

int d[200050];

long long dist[100005];

queue<int> que;

bool vis[100050],inq[100050];



bool spfa(int st)

{

    que.push(st);

    inq[st] = true;

    dist[st] = 0;

    long long curmax = 0,curmin = 0;

    while(!que.empty())

    {

        int u = que.front();

        que.pop();

        for(int i = head[u];i;i = e[i].nxt)

        {

            int v = e[i].v;

            if(dist[v] == 12345678987654321)

            {

                dist[v] = dist[u] + e[i].w;

                curmax = max(curmax,dist[v]);

                curmin = min(curmin,dist[v]);

                if(curmax - curmin > 1000000000)

                    return false;

                vis[v] = 1;

                if(!inq[v])

                    que.push(v);

            }

            else if(dist[v] != dist[u] + e[i].w)

                return false;

        }

        inq[u] = false;

    }

    return true;

}



int main()

{

    for(int i = 1;i <= 100000; ++ i)

        dist[i] = 12345678987654321;

    scanf("%d%d",&N,&M);

    for(int i = 1;i <= M; ++ i)

    {

        scanf("%d%d%d",&l[i],&r[i],&d[i]);

        adde(l[i],r[i],d[i]);

        adde(r[i],l[i],-d[i]);

    }

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

    {

        if(!vis[i])

        {

            vis[i] = 1;bool l = spfa(i);

            if(!l)

            {

                printf("No\n");

                return 0;

            }

        }

    }

    printf("Yes\n");

    return 0;

}

// Faster C++ solution for the same problem (measured speedup: 1.7612221969391406x):
#include <cstdio>

#include <iostream>

#include <algorithm>

#include <cstring>

#include <queue>

#define N 100001

#define M 200001

#define INF 0x7fffffff

#define ri register int

#define in inline

using namespace std;

struct edge

{

	int to,w,next;

}e[M<<1];

//vis数组表示已松弛的点

//book数组表示进入队列的点 

int head[N],vis[N],dis[N];

int m,n;

bool book[N];

in bool spfa(int s);

int main()

{

	ios::sync_with_stdio(false);

	int u,v,w;

	cin>>n>>m;

	for(ri i=1;i<=m;i++)

	{

		cin>>u>>v>>w;

		e[i]=(edge) {v,w,head[u]}; head[u]=i;

		e[i+m]=(edge) {u,-w,head[v]}; head[v]=i+m;

	}

	//一组数据为一个问题，不能将fill数组放在spfa里 

	fill(dis+1,dis+n+1,INF);

	//所有约束条件需要考虑到

	//可能有多个分块，前10个约束条件与后10个 无关联 

	for(ri i=1;i<=n;i++)

		if(!vis[i])

		{

			if(spfa(i))

			{cout<<"No"; return 0;}

			vis[i]=1;

		}	

	cout<<"Yes";

	return 0;

}

in bool spfa(int s)

{

	queue <int> q;

	int k;

	q.push(s); book[s]=1;

	dis[s]=0;

	while(q.size())

	{

		k=q.front(); q.pop();

		book[k]=0;//出队列应置为0 

		for(ri i=head[k]; i ;i=e[i].next)

		{

			int v=e[i].to;

			if(!vis[v]&& dis[k]<INF)

			{

				dis[v]=dis[k]+e[i].w;

				if(!book[v])

				{q.push(v); book[v]=1;}

				vis[v]=1;

			}

			else if(dis[k]<INF && dis[v]<INF && dis[v]!=dis[k]+e[i].w) return true;

			//此时相当于自相矛盾

			//思想：即先认定一开始所给条件均正确（此时松弛）

		    //最后再来验证接下来的条件是否满足原先条件 

		}

	}

	return false;

}