#include <bits/stdc++.h>
using namespace std;
using ll = long long;
// --------------------------------------------------------
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; }
#define FOR(i,l,r) for (int i = (l); i < (r); ++i)
#define RFOR(i,l,r) for (int i = (r)-1; (l) <= i; --i)
#define REP(i,n) FOR(i,0,n)
#define RREP(i,n) RFOR(i,0,n)
#define ALL(c) (c).begin(), (c).end()
#define RALL(c) (c).rbegin(), (c).rend()
#define SORT(c) sort(ALL(c))
#define RSORT(c) sort(RALL(c))
#define MIN(c) *min_element(ALL(c))
#define MAX(c) *max_element(ALL(c))
#define SUM(c) accumulate(ALL(c), 0)
#define SUMLL(c) accumulate(ALL(c), 0LL)
#define SZ(c) ((int)(c).size())
#define CIN(c) cin >> (c)
#define COUT(c) cout << (c) << '\n'
#define debug(x) cerr << #x << " = " << (x) << '\n';
using P = pair<int,int>;
using VP = vector<P>;
using VVP = vector<VP>;
using VS = vector<string>;
using VI = vector<int>;
using VVI = vector<VI>;
using VLL = vector<ll>;
using VVLL = vector<VLL>;
using VB = vector<bool>;
using VVB = vector<VB>;
using VD = vector<double>;
using VVD = vector<VD>;
static const double EPS = 1e-10;
static const double PI  = acos(-1.0);
static const ll MOD = 1000000007;
// static const ll MOD = 998244353;
static const int INF = (1 << 30) - 1;  // 1073741824 - 1
// static const ll INF = (1LL << 62) - 1;  // 4611686018427387904 - 1
// --------------------------------------------------------
// #include <atcoder/all>
// using namespace atcoder;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout << fixed << setprecision(15);

    int N, M; cin >> N >> M;
    VVP G(N+1);
    REP(_,M) {
        int u, v, c; cin >> u >> v >> c;
        G[u].push_back(P(v, c));
        G[v].push_back(P(u, c));
    }

    const int root = 1;

    VVP H(N+1);
    VB used(N+1, false);
    used[root] = true;
    queue<int> q;
    q.push(root);
    while (!q.empty()) {
        int u = q.front(); q.pop();
        for (auto p : G[u]) {
            int v = p.first;
            int c = p.second;
            if (used[v]) continue;
            used[v] = true;
            q.push(v);
            H[u].push_back(P(v, c));
        }
    }

    VI ans(N+1);
    ans[root] = 1;
    q.push(root);
    while (!q.empty()) {
        int u = q.front(); q.pop();
        for (auto p : H[u]) {
            int v = p.first;
            int c = p.second;
            if (ans[u] == c) {
                ans[v] = (c == 1) ? N : 1;
            } else {
                ans[v] = c;
            }
            q.push(v);
        }
    }

    FOR(i,1,N+1) {
        COUT(ans[i]);
    }

    return 0;
}

#include<bits/stdc++.h>
using namespace std;

#ifndef ONLINE_JUDGE
#define DEBUG
//#define STRESS
#endif // ONLINE_JUDGE

template <typename T>
ostream& operator<<(ostream& os, const vector<T>& v)
{
    os << "[";
    for (int i = 0; i < v.size(); ++i)
    {
        os << v[i];
        if (i != v.size() - 1)
            os << ", ";
    }
    os << "]";
    return os;
}

template <typename T, typename U>
ostream& operator<<(ostream& os, const pair<T, U>& p)
{
    os << "[";
    os << p.first;
    os << ", ";
    os << p.second;
    os << "]";
    return os;
}

vector<long long> dp;

struct constraint
{
    //int xElements; // under the first x Elements
    int atMostZ;   // atMostZ can be
    int NumY;    // Less or equal to num
};

bool checkConstraints(vector<vector<constraint>>& cons, int state)
{
    int cntAll = __builtin_popcount(state);
    for(constraint& con : cons[cntAll])
    {
        int cntSmallerEqualY = __builtin_popcount(state & ((1 << con.NumY) - 1));
        if(cntSmallerEqualY > con.atMostZ)
        {
            return false;
        }
    }
    return true;
}

int n = 0, m = 0;

void doDp(vector<vector<constraint>>& cons, int num)
{

    long long sum = 0;
    if(checkConstraints(cons, num))
    {
        //cout << "PASS ";
        for(int i = 0; i < n; i++)
        {
            if(num & 1 << i)
            {
                sum += dp[num ^ (1 << i)];
            }
        }
    }
    //cout << std::bitset<18>(num) << " :" << sum << "\n";
    dp[num] = sum;
}

int solve()
{
    cin >> n >> m;
    vector<vector<constraint>> cons(n + 1, vector<constraint>(0));
    for(int i = 0; i < m; ++i)
    {
        constraint con;
        int xelement;
        cin >> xelement >> con.NumY >> con.atMostZ;
        cons[xelement].push_back(con);
    }
    dp.resize(1<<n);
    dp[0] = 1;
    for(int i = 1; i < 1 << n; i++)
    {
        doDp(cons, i);
    }
    vector<bool> ans(n, true);
    cout << dp[(1 << n) - 1] << "\n";

    return 0;
}


int main()
{
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
#ifndef ONLINE_JUDGE
    freopen("input.txt", "r", stdin);
    //freopen("a.out","w",stdout); // console output is better (in most cases)
#else
    // add i/o method of specific testing system
#endif
    {
        solve();
    }
    return 0;
}
