//g++ -std=c++14 test.cpp -o test.out

#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <cassert>
#include <algorithm>
#include <functional>
#include <iostream>
#include <iomanip>
#include <vector>
#include <queue>
#include <stack>
#include <map> 
#include <unordered_map> 
#include <utility>
#include <limits.h>
#include <bitset>
#include <set>
using namespace std;
 
#define LL long long int
const LL INF = (1LL<<60);
const int INF_INT = 2147483647-1e6-1;
const LL mod = 1000000007ll;
const int mod_int = 1000000007;

//引用元
//http://drken1215.hatenablog.com/entry/2019/03/03/224600
//union くっつける
//find  どのグループか調べる
struct UnionFind {
    vector<LL> parent;
    
    UnionFind(LL n) : parent(n, -1) { }
    void init(LL n) { parent.assign(n, -1); }
    
    //xの属している木の根っこを返す
    LL root(LL x) {
        if (parent[x] < 0) return x;
        else return parent[x] = root(parent[x]);//経路圧縮
    }
    
    LL size(LL x) {return -parent[root(x)];}//xの属する木の要素数を求める
    bool is_same_tree(LL x,LL y) {return root(x)==root(y);}
    
    bool merge(LL x,LL y) {
        x = root(x); y = root(y);
        if (x == y) return false;
        if (parent[x] > parent[y]) swap(x, y);//xの親の方がyの親より小さくなるようにする
        parent[x] += parent[y];//経路の長さを加算
        parent[y] = x;//yの親をxとする
        return true;
    }
};
 
LL N,M;
LL U[200000],V[200000],C[200000];

LL ans[200001];

vector<vector<pair<LL,LL>>> adj;
void rec(LL now_node=1,LL prev_node=-1){
    for(auto next:adj[now_node]){
        LL next_node = next.first;
        LL edge_num = next.second;
        if(next_node!=prev_node){
            if(ans[now_node]==edge_num){
                if(ans[now_node]==1) ans[next_node] = 2;
                else ans[next_node] = 1;
            }else{
                ans[next_node] = edge_num;
            }
            rec(next_node,now_node);
        }
    }
}
 
void solve(){
    UnionFind uf(N+1);
    //i->jの辺が存在して、それの番号がcならadj[i]はpair(j,c)を含む
    adj = vector<vector<pair<LL,LL>>>(N+1);
    for(int i=0;i<M;i++){
        if(!uf.is_same_tree(U[i],V[i])){
            uf.merge(U[i],V[i]);
            adj[U[i]].push_back(pair<LL,LL>(V[i],C[i]));
            adj[V[i]].push_back(pair<LL,LL>(U[i],C[i]));
        }
    }
    ans[1] = 1;
    rec();
}
 
int main(){
    cin >> N >> M;
    for(int i=0;i<M;i++){
        LL u,v,c; cin >> u >> v >> c;
        U[i] = u;
        V[i] = v;
        C[i] = c;
    }
 
    solve();
 
    for(int i=1;i<=N;i++) cout << ans[i] << endl;
    return 0;
}
#include<bits/stdc++.h>

using namespace std;
typedef long long ll;

#define rep(i,n) for (long long i = 0; i < (n); ++i)
#define DIV 1000000007 //10^9+7
#define INF LONG_MAX/3
#define bit(n) (1LL<<(n))

template<class T>bool chmax(T &a, const T &b) { if (a<b) { a=b; return 1; } return 0; }
template<class T>bool chmin(T &a, const T &b) { if (b<a) { a=b; return 1; } return 0; }
int dx[4]={1,0,-1,0};
int dy[4]={0,1,0,-1};

// pos, label
vector<pair<ll, ll> > to[100005];
ll ans[100005];

	ll N, M;

	void dfs(ll cur, ll pare, bool done) {
		if(ans[cur] != -1) return;
		if(done) {
			ans[cur] = pare + 1;
			if(ans[cur] > N) {
				ans[cur] = pare -1;
			}
		} else {
			ans[cur] = pare;
		}

		for(auto next: to[cur]) {
			dfs(next.first, next.second, next.second == ans[cur]);
		}


	}
set<ll> root[100005];

int main(){
	cin >> N >> M;
	rep(i, M) {
		ll u, v, c;
		cin >> u >> v >> c;
		u--;v--;

		root[u].insert(c);
		root[v].insert(c);

		to[u].push_back(make_pair(v, c));
		to[v].push_back(make_pair(u, c));
	}
	rep(i, N) ans[i] = -1;
	dfs(0, 1, false);
	rep(i, N) cout << ans[i] << endl;;
}
