#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
using namespace std;
///Welcome to Nasif's Code
#define bug printf("bug\n");
#define bug2(var) cout<<#var<<" "<<var<<endl;
#define co(q) cout<<q<<endl;
#define all(q) (q).begin(),(q).end()
#define pi acos(-1)
#define inf 1000000000000000LL
#define FastRead    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#define MODADD(ADD_X,ADD_Y)  (ADD_X+ADD_Y)%MOD
#define MODSUB(SUB_X,SUB_Y)  ((SUB_X-SUB_Y)+MOD)%MOD
#define MODMUL(MUL_X,MUL_Y)  (MUL_X*MUL_Y)%MOD
#define LCM(LCM_X,LCM_Y) (LCM_X/__gcd(LCM_X,LCM_Y))*LCM_Y
template <typename T> using ordered_set = tree<T, null_type, less<T>, rb_tree_tag, tree_order_statistics_node_update>;
typedef long long int ll;
typedef unsigned long long int ull;
const int MOD = (int)1e9+7;
const int MAX = 1e6;
int dx[]= {1,0,-1,0,1,-1,1,-1};
int dy[]= {0,1,0,-1,1,-1,-1,1};
int parent[MAX+50],fam[MAX+50],ans[MAX],dep[MAX];
vector<pair<int,int> >adj[MAX];
int root(int child)
{
    while(parent[child]!=child)
    {
        parent[child]=parent[parent[child]];
        child=parent[child];
    }
    return child;
}
void node_union(int x,int y)
{
    int a=root(x);
    int b=root(y);
    parent[min(a,b)]=parent[max(a,b)];
}
void dfs(int src,int par,int h)
{
    dep[src]=h+1;
    if(ans[src]==ans[par] && par!=0)
    {
        int id=1;
        if(ans[par]==id)
            id++;
        for(auto it:adj[src])
        {
            if(it.second==id)
                id++;
        }
        ans[src]=id;
    }
    for(auto it:adj[src])
    {
        if(it.first==par)
            continue;
        ans[it.first]=it.second;
        dfs(it.first,src,h+1);
    }
}
int main()
{
    FastRead
    //freopen("output.txt", "w", stdout);
    for(int i=1; i<=MAX; i++)
        parent[i]=i;
    int n,m;
    cin>>n>>m;
    int cnt=0;
    for(int i=0; i<m; i++)
    {
        int a,b,c;
        cin>>a>>b>>c;
        if(root(a)!=root(b))
        {
            cnt++;
            adj[a].push_back({b,c});
            adj[b].push_back({a,c});
            node_union(a,b);
        }
    }
    if(cnt!=n-1)
    {
        cout<<"No"<<endl;
        return 0;
    }
    dfs(1,0,0);
    int id=1;
    for(auto i:adj[1])
        if(i.second==id)
            id++;
    ans[1]=id;
    for(int i=1; i<=n; i++)
        cout<<ans[i]<<endl;

    return 0;
}

#include<bits/stdc++.h>
using namespace std;
 
#define mp          make_pair
#define pb          push_back
#define ff          first
#define ss          second
#define all(x)      x.begin(),x.end()
#define rall(x)     x.rbegin(),x.rend()
#define gcd(a,b)    __gcd((a),(b))
#define lcm(a,b)    ((a)*(b))/gcd((a),(b))
#define endl        "\n"
#define pi          3.14159265358979323846
#define inf         1000000000
#define setbits(x)  __builtin_popcountll(x)
#define ll          long long
#define int         long long
#define ld          long double
#define lb          lower_bound
#define ub          upper_bound
 
 
#define fio ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);
 
const int N = 2e5+50;
const ll INF = 1e9;
 
// int dx[9]={-1,-1,-1,0,1,1,1,0,0};
// int dy[9]={-1,0,1,1,1,0,-1,-1,0};
int dx[] = {-1,0,1,0};
int dy[] = {0,1,0,-1};

//only for prime m

template<typename T>
void __print(int x) {cerr << x;}
void __print(long x) {cerr << x;}
void __print(long long x) {cerr << x;}
void __print(unsigned x) {cerr << x;}
void __print(unsigned long x) {cerr << x;}
void __print(unsigned long long x) {cerr << x;}
void __print(float x) {cerr << x;}
void __print(double x) {cerr << x;}
void __print(long double x) {cerr << x;}
void __print(char x) {cerr << '\'' << x << '\'';}
void __print(const char *x) {cerr << '\"' << x << '\"';}
void __print(const string &x) {cerr << '\"' << x << '\"';}
void __print(bool x) {cerr << (x ? "true" : "false");}
 
template<typename T, typename V>
void __print(const pair<T, V> &x) {cerr << '{'; __print(x.first); cerr << ','; __print(x.second); cerr << '}';}
template<typename T>
void __print(const T &x) {int f = 0; cerr << '{'; for (auto &i: x) cerr << (f++ ? "," : ""), __print(i); cerr << "}";}
void _print() {cerr << "]\n";}
template <typename T, typename... V>
void _print(T t, V... v) {__print(t); if (sizeof...(v)) cerr << ", "; _print(v...);}
#ifndef ONLINE_JUDGE
#define debug(x...) cerr << "[" << #x << "] = ["; _print(x)
#else
#define debug(x...)
#endif
const int mod = 1e9 + 7;


bool compare(const pair<ll, ll>&i, const pair<ll, ll>&j){ return i.second < j.second; }
ll modexp(ll x,ll n){ ll r=1; while(n){ if(n&1)r=(r*x)%mod; x=(x*x)%mod; n>>=1;} return r%mod;}
ll modinv(ll x){ return modexp(x,mod-2);}
// ll ncr(ll n,ll r){ if(n<r) return 0; ll ans=(fact[n]%mod * modinv(fact[r])%mod * modinv(fact[n-r])%mod)%mod; return ans;}
vector<ll>sieve(int n){int*arr=new int[n + 1](); vector<ll>vect; for(int i=2;i<=n;i++) if(arr[i]==0) {vect.push_back(i); for (int j=2*i;j<=n;j+=i) arr[j]=1;} return vect;}

ll mod_add(ll a,ll b){a=a%mod; b=b%mod; return (((a+b)%mod)+mod)%mod;}
ll mod_mul(ll a,ll b){a=a%mod; b=b%mod; return (((a*b)%mod)+mod)%mod;}
ll mod_sub(ll a,ll b){a=a%mod; b=b%mod; return (((a-b)%mod)+mod)%mod;}
ll mod_div(ll a,ll b){a=a%mod; b=b%mod; return (mod_mul(a,modinv(b))+mod)%mod;} 

int n,m;
vector<int>adj[2005];
vector<pair<int,int>>ans;
int vis[2005];

void dfs(int x,int par){
    ans.pb(mp(x,par));
    vis[x]=1;
    for(auto ch:adj[x]){
        if(ch!=par and !vis[ch]){
            dfs(ch,x);
        }
    }
}

void solve(){
    cin>>n>>m;
    for(int i=0;i<m;i++){
        int a,b;
        cin>>a>>b;
        adj[a].pb(b);
    }

    for(int i=1;i<=n;i++){
        memset(vis,0,sizeof(vis));
        dfs(i,i);
    }
    debug(ans);
    cout<<ans.size()<<endl;
    return ;

}   

int32_t main()
{
    fio
    // #ifdef ONLINE_JUDGE
    // freopen("input.txt","r",stdin);
    // freopen("output.txt","w",stdout);
    // #endif
 
    // pre();
    int t=1;
    // cin>>t; 
    for(int i=0;i<t;i++)
    {
        solve();
    }
    return 0;
}
 
