Problem:
Problem F
ICPC Teams
You are a coach of the International Collegiate Programming Contest (ICPC) club in your university. There are 3N students in the ICPC club and you want to make $N$ teams for the next ICPC. All teams in ICPC consist of 3 members. Every student belongs to exactly one team.
When you form the teams, you should consider several relationships among the students. Some student has an extremely good relationship with other students. If they belong to a same team, their performance will improve surprisingly. The contrary situation also occurs for a student pair with a bad relationship. In short, students with a good relationship must be in the same team, and students with a bad relationship must be in different teams. Since you are a competent coach, you know all $M$ relationships among the students.
Your task is to write a program that calculates the number of possible team assignments. Two assignments are considered different if and only if there exists a pair of students such that in one assignment they are in the same team and in the other they are not.
Input
The input consists of a single test case. The first line contains two integers $N$ $(1 \leq N \leq 10^6)$ and $M$ $(1 \leq M \leq 18)$. The $i$-th line of the following $M$ lines contains three integers $A_i$, $B_i$ $(1 \leq A_i, B_i \leq 3N, A_i \ne B_i)$, and $C_i$ $(C_i \in \{0, 1\})$. $A_i$ and $B_i$ denote indices of the students and $C_i$ denotes the relation type. If $C_i$ is 0, the $A_i$-th student and the $B_i$-th student have a good relation. If $C_i$ is 1, they have a bad relation. You can assume that $\{A_i, B_i\} \ne \{A_j, B_j\}$ if $i \ne j$ for all $1 \leq i, j \leq M$.
Output
Display a line containing the number of the possible team assignments modulo $10^9 + 9$.
Sample Input 1
2 2
1 2 0
3 4 1
Output for the Sample Input 1
2

Submitted solutions:

code_snippet: #include <bits/stdc++.h>

#define x first
#define y second
#define pb push_back
#define mp make_pair
#define vi vector<int>
#define pr pair<int,int>
#define Rep(i,a,b) for(int i=(int)(a);i<(int)(b);i++)
#define For(i,a,b) for(int i=(int)(a);i<=(int)(b);i++)
#define debug(x) {cout<<(#x)<<" "<<x<<endl;}

using namespace std;
typedef long long ll;

const int N=40,INF=9999999;
const ll P=1e9+9;

template<typename T>
inline void read(T& x,char s=getchar()) {
	x=0;
	while(s>'9'||s<'0') s=getchar();
	while(s<='9'&&s>='0') x=x*10+s-'0',s=getchar();
}

inline ll _pow(ll a,ll b,ll r=1) {
	while(b) {
		if(b&1) (r*=a)%=P;
		(a*=a)%=P;
		b>>=1;
	}
	return r;
}

vector<pr> a,b;
map<int,int> ma;
int cnt;
int c[N];
int fa[N];
ll js[3000005];
ll fq[3000005];

inline int find(int x) {
	return x==fa[x]?x:fa[x]=find(fa[x]);
}

inline void unionn(int x,int y) {
	int fx=find(x);
	int fy=find(y);
	if(fx!=fy) fa[fy]=fx;
}

int main() {
	int n,m,x,y,t;
	ll ans=0;
	cin>>n>>m;
	js[0]=fq[0]=1;
	for(int i=1;i<=3*n;i++) {
		js[i]=js[i-1]*i%P;
		if(i%3==0) {
			fq[i]=fq[i-3]*i*2%P; 
			//if(i<=10) cout<<fq[i]<<" ";
		}
	}
	
	for(int i=1;i<=m;i++) {
		cin>>x>>y>>t;
		if(ma.count(x)) x=ma[x];
		else ma[x]=++cnt,x=cnt;
		if(ma.count(y)) y=ma[y];
		else ma[y]=++cnt,y=cnt;
		
		if(t==0) {
			a.pb(mp(x,y));
		}
		else {
			b.pb(mp(x,y));
		}
	}
	for(int j=0;j<(1<<b.size());j++) {
		ll num=0;
		for(int i=1;i<=cnt;i++) fa[i]=i;
		for(int i=0;i<a.size();i++) {
			unionn(a[i].x,a[i].y);
		}
		for(int i=0;i<b.size();i++) {
			if(j&(1<<i)) {
				num++;
				unionn(b[i].x,b[i].y);
			}
		}
//		cout<<"num: "<<j<<" "<<num<<endl;
		num=num&1?-1:1;
		
//		cout<<num<<": ";
		memset(c,0,sizeof(c));
		for(int i=1;i<=cnt;i++) {
			c[find(i)]++;
		}
		int _n=n;
		int qindian=0;
		ll tot=1;
		for(int i=1;i<=cnt;i++) {
//			cout<<c[i]<<" ";
			if(c[i]>3) {
				tot=0;
//				cout<<endl;
				break;
			}
			if(c[i]==3) _n--;
			if(c[i]==2) qindian++;
		}
//		cout<<endl;
		
		if(tot==0) continue;
		
//		ll test=_pow(6,P-2)*6%P;
//		cout<<"-==>"<<test<<endl;
		
		tot=js[_n*3-qindian*2]*_pow(js[_n*3-qindian*3],P-2)%P;
//		cout<<"-->"<<_n*3-qindian*2<<" "<<_n*3-qindian*3<<endl;
//		cout<<"-->"<<js[_n*3-qindian*2]<<" "<<js[_n*3-qindian*3]<<endl;
//		cout<<"-->"<<tot<<endl;
		_n-=qindian;
//		cout<<"__n: "<<_n<<endl;
		tot=tot*js[_n*3]%P*_pow(fq[_n*3],P-2)%P;
//		cout<<"-->"<<tot<<endl;
		ans=(ans+P+num*tot)%P;
	}
	cout<<ans<<endl;
	return 0;
}
/*
2 3
1 2 0
3 5 0
3 4 1

2 2
1 2 0
3 4 1

10003 2
1 2 0
3 4 1
*/
accuracy: 1
time_ms: 100
memory_kb: 50236
final_rank: 3

code_snippet: // AOJ #2692 ICPC Teams
// 2025.3.21

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

const int NMAX = 1000005;
const int MOD = 1000000009;

int m0, m1, tot, ans;

int num[NMAX * 3];
int way[NMAX];
int uf[NMAX];
int compSize[40];

int uf_find(int x) {
    return uf[x] == x ? x : uf[x] = uf_find(uf[x]);
}

void uf_union(int x, int y) {
    x = uf_find(x);
    y = uf_find(y);
    if (x != y) uf[x] = y;
}

int a0[20], b0[20];
int a1[20], b1[20];
int sz[40];


int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);

    int N, M;
    cin >> N >> M;

    way[0] = 1;
    for (int i = 1; i <= N; i++) {
        ll t = (ll)(3 * i - 1) * (3 * i - 2) % MOD;
        t = t * ((MOD + 1) / 2) % MOD;
        way[i] = (int)( (ll) way[i - 1] * t % MOD );
    }

    for (int i = 1; i <= M; i++) {
        int u, v, c;
        cin >> u >> v >> c;
        if (num[u] == 0) num[u] = ++tot;
        if (num[v] == 0) num[v] = ++tot;
        if (c == 0) a0[++m0] = u, b0[m0] = v;
        else        a1[++m1] = u, b1[m1] = v;
    }

    for (int mask = 0; mask < (1 << m1); mask++) {
        int cnt = 0, tg = N, tp = N * 3, pr = 0, res = 1;
        bool ok = true;

        for (int i = 1; i <= tot; i++) uf[i] = i, sz[i] = 0;

        for (int i = 1; i <= m0; i++) uf_union(num[a0[i]], num[b0[i]]);

        for (int j = 0; j < m1; j++) {
            if (mask & (1 << j)) {
                cnt++;
                uf_union(num[a1[j + 1]], num[b1[j + 1]]);
            }
        }

        for (int i = 1; i <= tot; i++) sz[uf_find(i)]++;
        for (int i = 1; i <= tot; i++) {
            if (sz[i] > 3) {
                ok = false;
                break;
            }
            if (sz[i] > 1) {
                tg--;
                if (sz[i] == 2) pr++;
                tp -= sz[i];
            }
        }
        if (tg < 0 || !ok) continue;
        res = way[tg];
        for (int i = 1; i <= pr; i++) res = (int)((ll) res * (tp - i + 1) % MOD);

        if (cnt & 1) ans = (ans - res + MOD) % MOD;
        else ans = (ans + res) % MOD;
    }
    cout << ans << endl;
    return 0;
}
accuracy: 1
time_ms: 60
memory_kb: 19872
final_rank: 1

code_snippet: #include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b) for (int i = (int)(a); i < (int)(b); i++)
#define rrep(i, a, b) for (int i = (int)(b)-1; i >= (int)(a); i--)
#define ALL(v) (v).begin(), (v).end()
#define UNIQUE(v) sort(ALL(v)), (v).erase(unique(ALL(v)), (v).end())
#define SZ(v) (int)v.size()
#define MIN(v) *min_element(ALL(v))
#define MAX(v) *max_element(ALL(v))
#define LB(v, x) int(lower_bound(ALL(v), (x)) - (v).begin())
#define UB(v, x) int(upper_bound(ALL(v), (x)) - (v).begin())

using uint = unsigned int;
using ll = long long int;
using ull = unsigned long long;
using i128 = __int128_t;
using u128 = __uint128_t;
const int inf = 0x3fffffff;
const ll INF = 0x1fffffffffffffff;

template <typename T> inline bool chmax(T &a, T b) {
    if (a < b) {
        a = b;
        return 1;
    }
    return 0;
}
template <typename T> inline bool chmin(T &a, T b) {
    if (a > b) {
        a = b;
        return 1;
    }
    return 0;
}
template <typename T, typename U> T ceil(T x, U y) {
    assert(y != 0);
    if (y < 0)
        x = -x, y = -y;
    return (x > 0 ? (x + y - 1) / y : x / y);
}
template <typename T, typename U> T floor(T x, U y) {
    assert(y != 0);
    if (y < 0)
        x = -x, y = -y;
    return (x > 0 ? x / y : (x - y + 1) / y);
}
template <typename T> int popcnt(T x) {
    return __builtin_popcountll(x);
}
template <typename T> int topbit(T x) {
    return (x == 0 ? -1 : 63 - __builtin_clzll(x));
}
template <typename T> int lowbit(T x) {
    return (x == 0 ? -1 : __builtin_ctzll(x));
}

template <class T, class U>
ostream &operator<<(ostream &os, const pair<T, U> &p) {
    os << "P(" << p.first << ", " << p.second << ")";
    return os;
}
template <typename T> ostream &operator<<(ostream &os, const vector<T> &vec) {
    os << "{";
    for (int i = 0; i < vec.size(); i++) {
        os << vec[i] << (i + 1 == vec.size() ? "" : ", ");
    }
    os << "}";
    return os;
}
template <typename T, typename U>
ostream &operator<<(ostream &os, const map<T, U> &map_var) {
    os << "{";
    for (auto itr = map_var.begin(); itr != map_var.end(); itr++) {
        os << "(" << itr->first << ", " << itr->second << ")";
        itr++;
        if (itr != map_var.end())
            os << ", ";
        itr--;
    }
    os << "}";
    return os;
}
template <typename T> ostream &operator<<(ostream &os, const set<T> &set_var) {
    os << "{";
    for (auto itr = set_var.begin(); itr != set_var.end(); itr++) {
        os << *itr;
        ++itr;
        if (itr != set_var.end())
            os << ", ";
        itr--;
    }
    os << "}";
    return os;
}
#ifdef LOCAL
#define show(...) _show(0, #__VA_ARGS__, __VA_ARGS__)
#else
#define show(...) true
#endif
template <typename T> void _show(int i, T name) {
    cerr << '\n';
}
template <typename T1, typename T2, typename... T3>
void _show(int i, const T1 &a, const T2 &b, const T3 &...c) {
    for (; a[i] != ',' && a[i] != '\0'; i++)
        cerr << a[i];
    cerr << ":" << b << " ";
    _show(i + 1, a, c...);
}

/**
 * @brief template
 */

struct UnionFind{
    vector<int> par; int n;
    UnionFind(){}
    UnionFind(int _n):par(_n,-1),n(_n){}
    int root(int x){return par[x]<0?x:par[x]=root(par[x]);}
    bool same(int x,int y){return root(x)==root(y);}
    int size(int x){return -par[root(x)];}
    bool unite(int x,int y){
        x=root(x),y=root(y); if(x==y)return false;
        if(size(x)>size(y))swap(x,y);
        par[y]+=par[x]; par[x]=y; n--; return true;
    }
};

/**
 * @brief Union Find
 */

template <unsigned mod = 1000000007> struct fp {
    unsigned v;
    static constexpr int get_mod() {
        return mod;
    }
    constexpr unsigned inv() const {
        assert(v != 0);
        int x = v, y = mod, p = 1, q = 0, t = 0, tmp = 0;
        while (y > 0) {
            t = x / y;
            x -= t * y, p -= t * q;
            tmp = x, x = y, y = tmp;
            tmp = p, p = q, q = tmp;
        }
        if (p < 0)
            p += mod;
        return p;
    }
    constexpr fp(ll x = 0) : v(x >= 0 ? x % mod : (mod - (-x) % mod) % mod) {}
    fp operator-() const {
        return fp() - *this;
    }
    fp pow(ull t) {
        fp res = 1, b = *this;
        while (t) {
            if (t & 1)
                res *= b;
            b *= b;
            t >>= 1;
        }
        return res;
    }
    fp &operator+=(const fp &x) {
        if ((v += x.v) >= mod)
            v -= mod;
        return *this;
    }
    fp &operator-=(const fp &x) {
        if ((v += mod - x.v) >= mod)
            v -= mod;
        return *this;
    }
    fp &operator*=(const fp &x) {
        v = ull(v) * x.v % mod;
        return *this;
    }
    fp &operator/=(const fp &x) {
        v = ull(v) * x.inv() % mod;
        return *this;
    }
    fp operator+(const fp &x) const {
        return fp(*this) += x;
    }
    fp operator-(const fp &x) const {
        return fp(*this) -= x;
    }
    fp operator*(const fp &x) const {
        return fp(*this) *= x;
    }
    fp operator/(const fp &x) const {
        return fp(*this) /= x;
    }
    bool operator==(const fp &x) const {
        return v == x.v;
    }
    bool operator!=(const fp &x) const {
        return v != x.v;
    }
    friend istream &operator>>(istream &is, fp &x) {
        return is >> x.v;
    }
    friend ostream &operator<<(ostream &os, const fp &x) {
        return os << x.v;
    }
};

template <typename T> T Inv(ll n) {
    static const int md = T::get_mod();
    static vector<T> buf({0, 1});
    assert(n > 0);
    n %= md;
    while (SZ(buf) <= n) {
        int k = SZ(buf), q = (md + k - 1) / k;
        buf.push_back(buf[k * q - md] * q);
    }
    return buf[n];
}

template <typename T> T Fact(ll n, bool inv = 0) {
    static const int md = T::get_mod();
    static vector<T> buf({1, 1}), ibuf({1, 1});
    assert(n >= 0 and n < md);
    while (SZ(buf) <= n) {
        buf.push_back(buf.back() * SZ(buf));
        ibuf.push_back(ibuf.back() * Inv<T>(SZ(ibuf)));
    }
    return inv ? ibuf[n] : buf[n];
}

template <typename T> T nPr(int n, int r, bool inv = 0) {
    if (n < 0 || n < r || r < 0)
        return 0;
    return Fact<T>(n, inv) * Fact<T>(n - r, inv ^ 1);
}
template <typename T> T nCr(int n, int r, bool inv = 0) {
    if (n < 0 || n < r || r < 0)
        return 0;
    return Fact<T>(n, inv) * Fact<T>(r, inv ^ 1) * Fact<T>(n - r, inv ^ 1);
}
template <typename T> T nHr(int n, int r, bool inv = 0) {
    return nCr<T>(n + r - 1, r, inv);
}

/**
 * @brief Modint
 */

using Fp = fp<1000000009>;

int main() {
    int N, M;
    cin >> N >> M;
    vector<pair<int,int>> A;
    vector<pair<int,int>> B;
    vector<int> S;
    rep(i,0,M) {
        int X, Y, Z;
        cin >> X >> Y >> Z;
        X--, Y--;
        S.push_back(X);
        S.push_back(Y);
        if (Z == 0) A.push_back({X,Y});
        else B.push_back({X,Y});
    }
    UNIQUE(S);
    int L = S.size();
    rep(i,0,A.size()) A[i] = {LB(S,A[i].first), LB(S,A[i].second)};
    rep(i,0,B.size()) B[i] = {LB(S,B[i].first), LB(S,B[i].second)};
    int K = B.size();
    Fp ANS = 0;
    rep(i,0,1<<K) {
        UnionFind UF(L);
        rep(j,0,A.size()) {
            UF.unite(A[j].first, A[j].second);
        }
        rep(j,0,B.size()) {
            if (i & (1<<j)) UF.unite(B[j].first, B[j].second);
        }
        vector<int> COUNT(5,0);
        COUNT[1] = N*3-L;
        rep(j,0,L) {
            if (UF.root(j) == j) {
                if (UF.size(j) >= 4) COUNT[4]++;
                else COUNT[UF.size(j)]++;
            }
        }
        if (COUNT[4] > 0) continue;
        if (COUNT[2] > COUNT[1]) continue;
        Fp MUL = Fact<Fp>(COUNT[1]) * Fact<Fp>(COUNT[1]-COUNT[2],true);
        COUNT[1] -= COUNT[2];
        int X = COUNT[1]/3;
        MUL *= Fact<Fp>(COUNT[1]);
        MUL /= Fp(6).pow(X);
        MUL *= Fact<Fp>(X,true);
        if (popcnt(i) % 2 == 0) ANS += MUL;
        else ANS -= MUL;
    }
    cout << ANS << endl;
}
accuracy: 1
time_ms: 150
memory_kb: 43884
final_rank: 4

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define rep(i, s, t) for (int i = (int)(s); i < (int)(t); ++i)
#define revrep(i, t, s) for (int i = (int)(t)-1; i >= (int)(s); --i)
#define all(x) begin(x), end(x)
template <typename T> bool chmax(T& a, const T& b) { return a < b ? (a = b, 1) : 0; }
template <typename T> bool chmin(T& a, const T& b) { return a > b ? (a = b, 1) : 0; }

template <typename M>
class UndoableUnionFind {
    using T = typename M::T;

public:
    UndoableUnionFind() = default;
    explicit UndoableUnionFind(int n) : UndoableUnionFind(std::vector<T>(n, M::id())) {}
    explicit UndoableUnionFind(const std::vector<T>& v)
        : data(v.size(), -1), val(v.begin(), v.end()) {}

    int find(int x) const {
        if (data[x] < 0) return x;
        return find(data[x]);
    }

    void unite(int x, int y) {
        x = find(x);
        y = find(y);
        history.emplace(-1, 0, M::id());
        history.emplace(x, data[x], val[x]);
        history.emplace(y, data[y], val[y]);
        if (x == y) return;
        if (data[x] > data[y]) std::swap(x, y);
        data[x] += data[y];
        data[y] = x;
        val[x] = M::op(val[x], val[y]);
    }

    void undo() {
        assert(!history.empty());
        while (true) {
            auto [x, dx, vx] = history.top();
            history.pop();
            if (x == -1) break;
            data[x] = dx;
            val[x] = vx;
        }
    }

    bool same(int x, int y) const {
        return find(x) == find(y);
    }

    int size(int x) const {
        return -data[find(x)];
    }

    T component_fold(int x) const {
        return val[find(x)];
    }

    void update(int x, const T& v) {
        x = find(x);
        history.emplace(-1, 0, M::id());
        history.emplace(x, data[x], val[x]);
        val[x] = M::op(val[x], v);
    }

private:
    std::vector<int> data;
    std::vector<T> val;
    std::stack<std::tuple<int, int, T>> history;
};

template <int mod>
class Modint {
    using mint = Modint;
    static_assert(mod > 0, "Modulus must be positive");

public:
    static constexpr int get_mod() noexcept { return mod; }

    constexpr Modint(long long y = 0) noexcept : x(y >= 0 ? y % mod : (y % mod + mod) % mod) {}

    constexpr int value() const noexcept { return x; }

    constexpr mint& operator+=(const mint& r) noexcept { if ((x += r.x) >= mod) x -= mod; return *this; }
    constexpr mint& operator-=(const mint& r) noexcept { if ((x += mod - r.x) >= mod) x -= mod; return *this; }
    constexpr mint& operator*=(const mint& r) noexcept { x = static_cast<int>(1LL * x * r.x % mod); return *this; }
    constexpr mint& operator/=(const mint& r) noexcept { *this *= r.inv(); return *this; }

    constexpr mint operator-() const noexcept { return mint(-x); }

    constexpr mint operator+(const mint& r) const noexcept { return mint(*this) += r; }
    constexpr mint operator-(const mint& r) const noexcept { return mint(*this) -= r; }
    constexpr mint operator*(const mint& r) const noexcept { return mint(*this) *= r; }
    constexpr mint operator/(const mint& r) const noexcept { return mint(*this) /= r; }

    constexpr bool operator==(const mint& r) const noexcept { return x == r.x; }
    constexpr bool operator!=(const mint& r) const noexcept { return x != r.x; }

    constexpr mint inv() const noexcept {
        int a = x, b = mod, u = 1, v = 0;
        while (b > 0) {
            int t = a / b;
            std::swap(a -= t * b, b);
            std::swap(u -= t * v, v);
        }
        return mint(u);
    }

    constexpr mint pow(long long n) const noexcept {
        mint ret(1), mul(x);
        while (n > 0) {
            if (n & 1) ret *= mul;
            mul *= mul;
            n >>= 1;
        }
        return ret;
    }

    friend std::ostream& operator<<(std::ostream& os, const mint& r) {
        return os << r.x;
    }

    friend std::istream& operator>>(std::istream& is, mint& r) {
        long long t;
        is >> t;
        r = mint(t);
        return is;
    }

private:
    int x;
};

using mint = Modint<(int) 1e9+9>;

template <typename T>
class Combination {
public:
    Combination() = default;
    Combination(int n) : fact_(n + 1), fact_inv_(n + 1) {
        fact_[0] = 1;
        for (int i = 1; i <= n; ++i) fact_[i] = fact_[i - 1] * i;
        fact_inv_[n] = T(1) / fact_[n];
        for (int i = n; i > 0; --i) fact_inv_[i - 1] = fact_inv_[i] * i;
    }

    T perm(int n, int r) const {
        if (r < 0 || n < r) return 0;
        return fact_[n] * fact_inv_[n - r];
    }

    T comb(int n, int r) const {
        if (r < 0 || n < r) return 0;
        return fact_[n] * fact_inv_[r] * fact_inv_[n - r];
    }

    T fact(int n) const { return fact_[n]; }
    T fact_inv(int n) const { return fact_inv_[n]; }

private:
    std::vector<T> fact_, fact_inv_;
};

struct Monoid {
    using T = int;
    static T id() { return 0; }
    static T op(T a, T b) { return 0; }
};


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

    int N, M;
    cin >> N >> M;
    vector<pair<int, int>> good, bad;
    rep(i,0,M) {
        int A, B, C;
        cin >> A >> B >> C;
        --A, --B;
        if (C) bad.push_back({A, B});
        else good.push_back({A, B});
    }
    int n = bad.size();
    mint ans = 0;

    vector<mint> pow6(3*N+1, 1), fact(3*N+1, 1);
    rep(i,1,3*N+1) {
        pow6[i] = pow6[i-1] * 6;
        fact[i] = fact[i-1] * i;
    }

    UndoableUnionFind<Monoid> uf(3*N);
    vector<int> cnt(3*N+1);
    cnt[1] = 3*N;
    for (auto [a, b] : good) {
        if (!uf.same(a, b)) {
            --cnt[uf.size(a)];
            --cnt[uf.size(b)];
            uf.unite(a, b);
            ++cnt[uf.size(a)];
        }
    }

    rep(S,0,1<<n) {
        vector<pair<int, int>> used;
        rep(i,0,n) {
            if (S>>i&1) {
                auto [a, b] = bad[i];
                if (!uf.same(a, b)) {
                    used.push_back({a, b});
                    --cnt[uf.size(a)];
                    --cnt[uf.size(b)];
                    uf.unite(a, b);
                    ++cnt[uf.size(a)];
                }
            }
        }
        int c = cnt[1] + 2*cnt[2] + 3*cnt[3];
        int d = cnt[1] - cnt[2];
        if (c == 3*N && d >= 0) {
            mint res = fact[cnt[1]] / (pow6[d/3] * fact[d/3]);
            if (__builtin_popcount(S) % 2 == 0) ans += res;
            else ans -= res;
        }
        reverse(all(used));
        for (auto [a, b] : used) {
            --cnt[uf.size(a)];
            uf.undo();
            ++cnt[uf.size(a)];
            ++cnt[uf.size(b)];
        }
    }
    cout << ans << endl;
}
accuracy: 1
time_ms: 130
memory_kb: 61600
final_rank: 5

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define rep(i, s, t) for (int i = (int)(s); i < (int)(t); ++i)
#define revrep(i, t, s) for (int i = (int)(t)-1; i >= (int)(s); --i)
#define all(x) begin(x), end(x)
template <typename T> bool chmax(T& a, const T& b) { return a < b ? (a = b, 1) : 0; }
template <typename T> bool chmin(T& a, const T& b) { return a > b ? (a = b, 1) : 0; }

template <typename M>
class UndoableUnionFind {
    using T = typename M::T;

public:
    UndoableUnionFind() = default;
    explicit UndoableUnionFind(int n) : UndoableUnionFind(std::vector<T>(n, M::id())) {}
    explicit UndoableUnionFind(const std::vector<T>& v)
        : data(v.size(), -1), val(v.begin(), v.end()) {}

    int find(int x) const {
        if (data[x] < 0) return x;
        return find(data[x]);
    }

    void unite(int x, int y) {
        x = find(x);
        y = find(y);
        history.emplace(-1, 0, M::id());
        history.emplace(x, data[x], val[x]);
        history.emplace(y, data[y], val[y]);
        if (x == y) return;
        if (data[x] > data[y]) std::swap(x, y);
        data[x] += data[y];
        data[y] = x;
        val[x] = M::op(val[x], val[y]);
    }

    void undo() {
        assert(!history.empty());
        while (true) {
            auto [x, dx, vx] = history.top();
            history.pop();
            if (x == -1) break;
            data[x] = dx;
            val[x] = vx;
        }
    }

    bool same(int x, int y) const {
        return find(x) == find(y);
    }

    int size(int x) const {
        return -data[find(x)];
    }

    T component_fold(int x) const {
        return val[find(x)];
    }

    void update(int x, const T& v) {
        x = find(x);
        history.emplace(-1, 0, M::id());
        history.emplace(x, data[x], val[x]);
        val[x] = M::op(val[x], v);
    }

private:
    std::vector<int> data;
    std::vector<T> val;
    std::stack<std::tuple<int, int, T>> history;
};

template <int mod>
class Modint {
    using mint = Modint;
    static_assert(mod > 0, "Modulus must be positive");

public:
    static constexpr int get_mod() noexcept { return mod; }

    constexpr Modint(long long y = 0) noexcept : x(y >= 0 ? y % mod : (y % mod + mod) % mod) {}

    constexpr int value() const noexcept { return x; }

    constexpr mint& operator+=(const mint& r) noexcept { if ((x += r.x) >= mod) x -= mod; return *this; }
    constexpr mint& operator-=(const mint& r) noexcept { if ((x += mod - r.x) >= mod) x -= mod; return *this; }
    constexpr mint& operator*=(const mint& r) noexcept { x = static_cast<int>(1LL * x * r.x % mod); return *this; }
    constexpr mint& operator/=(const mint& r) noexcept { *this *= r.inv(); return *this; }

    constexpr mint operator-() const noexcept { return mint(-x); }

    constexpr mint operator+(const mint& r) const noexcept { return mint(*this) += r; }
    constexpr mint operator-(const mint& r) const noexcept { return mint(*this) -= r; }
    constexpr mint operator*(const mint& r) const noexcept { return mint(*this) *= r; }
    constexpr mint operator/(const mint& r) const noexcept { return mint(*this) /= r; }

    constexpr bool operator==(const mint& r) const noexcept { return x == r.x; }
    constexpr bool operator!=(const mint& r) const noexcept { return x != r.x; }

    constexpr mint inv() const noexcept {
        int a = x, b = mod, u = 1, v = 0;
        while (b > 0) {
            int t = a / b;
            std::swap(a -= t * b, b);
            std::swap(u -= t * v, v);
        }
        return mint(u);
    }

    constexpr mint pow(long long n) const noexcept {
        mint ret(1), mul(x);
        while (n > 0) {
            if (n & 1) ret *= mul;
            mul *= mul;
            n >>= 1;
        }
        return ret;
    }

    friend std::ostream& operator<<(std::ostream& os, const mint& r) {
        return os << r.x;
    }

    friend std::istream& operator>>(std::istream& is, mint& r) {
        long long t;
        is >> t;
        r = mint(t);
        return is;
    }

private:
    int x;
};

using mint = Modint<(int) 1e9+9>;

template <typename T>
class Combination {
public:
    Combination() = default;
    Combination(int n) : fact_(n + 1), fact_inv_(n + 1) {
        fact_[0] = 1;
        for (int i = 1; i <= n; ++i) fact_[i] = fact_[i - 1] * i;
        fact_inv_[n] = T(1) / fact_[n];
        for (int i = n; i > 0; --i) fact_inv_[i - 1] = fact_inv_[i] * i;
    }

    T perm(int n, int r) const {
        if (r < 0 || n < r) return 0;
        return fact_[n] * fact_inv_[n - r];
    }

    T comb(int n, int r) const {
        if (r < 0 || n < r) return 0;
        return fact_[n] * fact_inv_[r] * fact_inv_[n - r];
    }

    T fact(int n) const { return fact_[n]; }
    T fact_inv(int n) const { return fact_inv_[n]; }

private:
    std::vector<T> fact_, fact_inv_;
};

struct Monoid {
    using T = int;
    static T id() { return 0; }
    static T op(T a, T b) { return 0; }
};


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

    int N, M;
    cin >> N >> M;
    vector<pair<int, int>> good, bad;
    rep(i,0,M) {
        int A, B, C;
        cin >> A >> B >> C;
        --A, --B;
        if (C) bad.push_back({A, B});
        else good.push_back({A, B});
    }
    int n = bad.size();
    mint ans = 0;

    vector<mint> pow6(3*N+1, 1), fact(3*N+1, 1);
    rep(i,1,3*N+1) {
        pow6[i] = pow6[i-1] * 6;
        fact[i] = fact[i-1] * i;
    }

    UndoableUnionFind<Monoid> uf(3*N);
    vector<int> cnt(3*N+1);
    cnt[1] = 3*N;
    for (auto [a, b] : good) {
        if (!uf.same(a, b)) {
            --cnt[uf.size(a)];
            --cnt[uf.size(b)];
            uf.unite(a, b);
            ++cnt[uf.size(a)];
        }
    }

    rep(S,0,1<<n) {
        rep(i,0,n) {
            if (S>>i&1) {
                auto [a, b] = bad[i];
                if (!uf.same(a, b)) {
                    --cnt[uf.size(a)];
                    --cnt[uf.size(b)];
                    uf.unite(a, b);
                    ++cnt[uf.size(a)];
                }
            }
        }
        int c = cnt[1] + 2*cnt[2] + 3*cnt[3];
        int d = cnt[1] - cnt[2];
        if (c == 3*N && d >= 0) {
            mint res = fact[cnt[1]] / (pow6[d/3] * fact[d/3]);
            if (__builtin_popcount(S) % 2 == 0) ans += res;
            else ans -= res;
        }
        revrep(i,n,0) {
            if (S>>i&1) {
                auto [a, b] = bad[i];
                --cnt[uf.size(a)];
                uf.undo();
                if (!uf.same(a, b)) {
                    ++cnt[uf.size(a)];
                    ++cnt[uf.size(b)];
                } else {
                    ++cnt[uf.size(a)];
                }
            }
        }
    }
    cout << ans << endl;
}
accuracy: 0.6875
time_ms: 110
memory_kb: 61544
final_rank: 15

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define rep(i, s, t) for (int i = (int)(s); i < (int)(t); ++i)
#define revrep(i, t, s) for (int i = (int)(t)-1; i >= (int)(s); --i)
#define all(x) begin(x), end(x)
template <typename T> bool chmax(T& a, const T& b) { return a < b ? (a = b, 1) : 0; }
template <typename T> bool chmin(T& a, const T& b) { return a > b ? (a = b, 1) : 0; }

template <typename M>
class UndoableUnionFind {
    using T = typename M::T;

public:
    UndoableUnionFind() = default;
    explicit UndoableUnionFind(int n) : UndoableUnionFind(std::vector<T>(n, M::id())) {}
    explicit UndoableUnionFind(const std::vector<T>& v)
        : data(v.size(), -1), val(v.begin(), v.end()) {}

    int find(int x) const {
        if (data[x] < 0) return x;
        return find(data[x]);
    }

    void unite(int x, int y) {
        x = find(x);
        y = find(y);
        history.emplace(-1, 0, M::id());
        history.emplace(x, data[x], val[x]);
        history.emplace(y, data[y], val[y]);
        if (x == y) return;
        if (data[x] > data[y]) std::swap(x, y);
        data[x] += data[y];
        data[y] = x;
        val[x] = M::op(val[x], val[y]);
    }

    void undo() {
        assert(!history.empty());
        while (true) {
            auto [x, dx, vx] = history.top();
            history.pop();
            if (x == -1) break;
            data[x] = dx;
            val[x] = vx;
        }
    }

    bool same(int x, int y) const {
        return find(x) == find(y);
    }

    int size(int x) const {
        return -data[find(x)];
    }

    T component_fold(int x) const {
        return val[find(x)];
    }

    void update(int x, const T& v) {
        x = find(x);
        history.emplace(-1, 0, M::id());
        history.emplace(x, data[x], val[x]);
        val[x] = M::op(val[x], v);
    }

private:
    std::vector<int> data;
    std::vector<T> val;
    std::stack<std::tuple<int, int, T>> history;
};

template <int mod>
class Modint {
    using mint = Modint;
    static_assert(mod > 0, "Modulus must be positive");

public:
    static constexpr int get_mod() noexcept { return mod; }

    constexpr Modint(long long y = 0) noexcept : x(y >= 0 ? y % mod : (y % mod + mod) % mod) {}

    constexpr int value() const noexcept { return x; }

    constexpr mint& operator+=(const mint& r) noexcept { if ((x += r.x) >= mod) x -= mod; return *this; }
    constexpr mint& operator-=(const mint& r) noexcept { if ((x += mod - r.x) >= mod) x -= mod; return *this; }
    constexpr mint& operator*=(const mint& r) noexcept { x = static_cast<int>(1LL * x * r.x % mod); return *this; }
    constexpr mint& operator/=(const mint& r) noexcept { *this *= r.inv(); return *this; }

    constexpr mint operator-() const noexcept { return mint(-x); }

    constexpr mint operator+(const mint& r) const noexcept { return mint(*this) += r; }
    constexpr mint operator-(const mint& r) const noexcept { return mint(*this) -= r; }
    constexpr mint operator*(const mint& r) const noexcept { return mint(*this) *= r; }
    constexpr mint operator/(const mint& r) const noexcept { return mint(*this) /= r; }

    constexpr bool operator==(const mint& r) const noexcept { return x == r.x; }
    constexpr bool operator!=(const mint& r) const noexcept { return x != r.x; }

    constexpr mint inv() const noexcept {
        int a = x, b = mod, u = 1, v = 0;
        while (b > 0) {
            int t = a / b;
            std::swap(a -= t * b, b);
            std::swap(u -= t * v, v);
        }
        return mint(u);
    }

    constexpr mint pow(long long n) const noexcept {
        mint ret(1), mul(x);
        while (n > 0) {
            if (n & 1) ret *= mul;
            mul *= mul;
            n >>= 1;
        }
        return ret;
    }

    friend std::ostream& operator<<(std::ostream& os, const mint& r) {
        return os << r.x;
    }

    friend std::istream& operator>>(std::istream& is, mint& r) {
        long long t;
        is >> t;
        r = mint(t);
        return is;
    }

private:
    int x;
};

using mint = Modint<(int) 1e9+9>;

template <typename T>
class Combination {
public:
    Combination() = default;
    Combination(int n) : fact_(n + 1), fact_inv_(n + 1) {
        fact_[0] = 1;
        for (int i = 1; i <= n; ++i) fact_[i] = fact_[i - 1] * i;
        fact_inv_[n] = T(1) / fact_[n];
        for (int i = n; i > 0; --i) fact_inv_[i - 1] = fact_inv_[i] * i;
    }

    T perm(int n, int r) const {
        if (r < 0 || n < r) return 0;
        return fact_[n] * fact_inv_[n - r];
    }

    T comb(int n, int r) const {
        if (r < 0 || n < r) return 0;
        return fact_[n] * fact_inv_[r] * fact_inv_[n - r];
    }

    T fact(int n) const { return fact_[n]; }
    T fact_inv(int n) const { return fact_inv_[n]; }

private:
    std::vector<T> fact_, fact_inv_;
};

struct Monoid {
    using T = int;
    static T id() { return 0; }
    static T op(T a, T b) { return 0; }
};


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

    int N, M;
    cin >> N >> M;
    vector<pair<int, int>> good, bad;
    rep(i,0,M) {
        int A, B, C;
        cin >> A >> B >> C;
        --A, --B;
        if (C) bad.push_back({A, B});
        else good.push_back({A, B});
    }
    int n = bad.size();
    mint ans = 0;

    vector<mint> pow6(3*N+1, 1), fact(3*N+1, 1);
    rep(i,1,3*N+1) {
        pow6[i] = pow6[i-1] * 6;
        fact[i] = fact[i-1] * i;
    }

    UndoableUnionFind<Monoid> uf(3*N);
    vector<int> cnt(3*N+1);
    cnt[1] = 3*N;
    for (auto [a, b] : good) {
        --cnt[uf.size(a)];
        --cnt[uf.size(b)];
        uf.unite(a, b);
        ++cnt[uf.size(a)];
    }

    rep(S,0,1<<n) {
        rep(i,0,n) {
            if (S>>i&1) {
                auto [a, b] = bad[i];
                --cnt[uf.size(a)];
                --cnt[uf.size(b)];
                uf.unite(a, b);
                ++cnt[uf.size(a)];
            }
        }
        int c = cnt[1] + 2*cnt[2] + 3*cnt[3];
        int d = cnt[1] - cnt[2];
        if (c == 3*N && d >= 0) {
            mint res = fact[cnt[1]] / (pow6[d/3] * fact[d/3]);
            if (__builtin_popcount(S) % 2 == 0) ans += res;
            else ans -= res;
        }
        revrep(i,n,0) {
            if (S>>i&1) {
                auto [a, b] = bad[i];
                --cnt[uf.size(a)];
                uf.undo();
                ++cnt[uf.size(a)];
                ++cnt[uf.size(b)];
            }
        }
    }
    cout << ans << endl;
}
accuracy: 0.7604166666666666
time_ms: 110
memory_kb: 61508
final_rank: 14

code_snippet: #define _GLIBCXX_DEBUG
#include <bits/stdc++.h>
using namespace std;

using ll = long long;
using pii = pair<int, int>;
using vi = vector<int>;

const int inf = 1 << 30;

#define rep(i, x) for(int i = 0; i < (x); i++)
#define sz(v)     (int)(v).size()
#define all(v)    (v).begin(), (v).end()

template <class T>
bool chmin(T &x, T y) {
	return x > y ? x = y, 1 : 0;
}

const int mod = 1000000009;

struct mint {
	ll v;
	mint(ll x = 0) {
		v = x % mod;
		if(v < 0) v += mod;
	}
	mint operator+(mint oth) { return v + oth.v; }
	mint operator-(mint oth) { return v - oth.v; }
	mint operator*(mint oth) { return v * oth.v; }
	mint pow(ll k) {
		mint ret = 1;
		mint a = v;
		while(k) {
			if(k & 1) ret = ret * a;
			a = a * a;
			k >>= 1;
		}
		return ret;
	}
	mint inv() { return pow(mod - 2); }
};


vector<mint> fac, finv;
void calc(int n) {
	fac.resize(n + 1, 1);
	finv.resize(n + 1, 1);
	rep(i, n) fac[i + 1] = fac[i] * (i + 1);
	finv.back() = fac.back().inv();
	for(int i = n; i >= 1; --i) finv[i - 1] = finv[i] * i;
}

mint binom(int n, int r) {
	if(n < 0 or r < 0 or n < r) return 0;
	return fac[n] * finv[r] * finv[n - r];
}

struct uf {
	vi e;
	uf(int n) : e(n, -1) {}
	int root(int x) {
		return e[x] < 0 ? x : e[x] = root(e[x]);
	}
	int size(int x) { return -e[root(x)]; }
	bool same(int u, int v) { return root(u) == root(v); }
	bool merge(int u, int v) {
		u = root(u), v = root(v);
		if(same(u, v)) return false;
		if(e[u] > e[v]) swap(u, v);
		e[u] += e[v];
		e[v] = u;
		return true;
	}
};


int main() {
	int n, m;
	cin >> n >> m;
	calc(3 * n);
	vector<int> a(m), b(m), c(m);
	vector<int> idx;
	set<int> st;
	rep(i, m) {
		cin >> a[i] >> b[i] >> c[i];
		--a[i], --b[i];
		st.insert(a[i]);
		st.insert(b[i]);
	}
	vi re;
	for(auto v : st) re.push_back(v);
	int k = sz(st);
	map<int, int> id;
	uf dsu(k);
	rep(i, m) {
		id[a[i]] = lower_bound(all(re), a[i]) - re.begin();
		id[b[i]] = lower_bound(all(re), b[i]) - re.begin();
		if(!c[i])
			dsu.merge(id[a[i]], id[b[i]]);
		else
			idx.push_back(i);
	}
	vector<mint> f(n + 1, 1);
	rep(i, n) {
		f[i + 1] = f[i] * binom(2 + 3 * i, 2);
	}
	mint ans = 0;
	rep(bit, 1 << sz(idx)) {
		auto tmp = dsu;
		bool pe = 0;
		rep(i, sz(idx)) {
			if(bit >> i & 1) {
				pe = !pe;
				tmp.merge(id[a[idx[i]]], id[b[idx[i]]]);
			}
		}
		vi tar;
		vi cnt(4);
		bool out = 0;
		rep(i, k) {
			if(i != tmp.root(i)) continue;
			if(tmp.size(i) > 3) {
				out = 1;
				break;
			}
			cnt[tmp.size(i)]++;
		}
		if(out or cnt[2] + cnt[3] > n) continue;
		int nokori = n * 3 - 2 * cnt[2] - 3 * cnt[3];
		mint res = 1;
		rep(i, cnt[2]) res = res * nokori--;
		res = res * f[nokori / 3];
		if(pe)
			ans = ans - res;
		else
			ans = ans + res;
	}
	cout << ans.v << endl;
}
accuracy: 1
time_ms: 180
memory_kb: 57624
final_rank: 6

code_snippet: #define _GLIBCXX_DEBUG
#include <bits/stdc++.h>
using namespace std;

using ll = long long;
using pii = pair<int, int>;
using vi = vector<int>;

const int inf = 1 << 30;

#define rep(i, x) for(int i = 0; i < (x); i++)
#define sz(v)     (int)(v).size()
#define all(v)    (v).begin(), (v).end()

template <class T>
bool chmin(T &x, T y) {
	return x > y ? x = y, 1 : 0;
}

const int mod = 1000000009;

struct mint {
	ll v;
	mint(ll x = 0) {
		v = x % mod;
		if(v < 0) v += mod;
	}
	mint operator+(mint oth) { return v + oth.v; }
	mint operator-(mint oth) { return v - oth.v; }
	mint operator*(mint oth) { return v * oth.v; }
	mint pow(ll k) {
		mint ret = 1;
		mint a = v;
		while(k) {
			if(k & 1) ret = ret * a;
			a = a * a;
			k >>= 1;
		}
		return ret;
	}
	mint inv() { return pow(mod - 2); }
};


vector<mint> fac, finv;
void calc(int n) {
	fac.resize(n + 1, 1);
	finv.resize(n + 1, 1);
	rep(i, n) fac[i + 1] = fac[i] * (i + 1);
	finv.back() = fac.back().inv();
	for(int i = n; i >= 1; --i) finv[i - 1] = finv[i] * i;
}

mint binom(int n, int r) {
	if(n < 0 or r < 0 or n < r) return 0;
	return fac[n] * finv[r] * finv[n - r];
}

struct uf {
	vi e;
	uf(int n) : e(n, -1) {}
	int root(int x) {
		return e[x] < 0 ? x : e[x] = root(e[x]);
	}
	int size(int x) { return -e[root(x)]; }
	bool same(int u, int v) { return root(u) == root(v); }
	bool merge(int u, int v) {
		u = root(u), v = root(v);
		if(same(u, v)) return false;
		if(e[u] > e[v]) swap(u, v);
		e[u] += e[v];
		e[v] = u;
		return true;
	}
};


int main() {
	int n, m;
	cin >> n >> m;
	calc(3 * n);
	vector<int> a(m), b(m), c(m);
	vector<int> idx;
	set<int> st;
	rep(i, m) {
		cin >> a[i] >> b[i] >> c[i];
		--a[i], --b[i];
		st.insert(a[i]);
		st.insert(b[i]);
	}
	vi re;
	for(auto v : st) re.push_back(v);
	int k = sz(st);
	map<int, int> id;
	uf dsu(k);
	rep(i, m) {
		id[a[i]] = lower_bound(all(re), a[i]) - re.begin();
		id[b[i]] = lower_bound(all(re), b[i]) - re.begin();
		if(!c[i])
			dsu.merge(id[a[i]], id[b[i]]);
		else
			idx.push_back(i);
	}
	vector<mint> f(n + 1, 1);
	rep(i, n) {
		f[i + 1] = f[i] * binom(2 + 3 * i, 2);
	}
	mint ans = 0;
	rep(bit, 1 << sz(idx)) {
		auto tmp = dsu;
		bool pe = 0;
		rep(i, sz(idx)) {
			if(bit >> i & 1) {
				pe = !pe;
				tmp.merge(id[a[idx[i]]], id[b[idx[i]]]);
			}
		}
		vi tar;
		vi cnt(4);
		bool out = 0;
		rep(i, k) {
			if(i != tmp.root(i)) continue;
			if(tmp.size(i) > 3) {
				out = 1;
				break;
			}
			cnt[tmp.size(i)]++;
		}
		if(out) continue;
		if(cnt[2] > n) continue;
		int nokori = n * 3 - 2 * cnt[2] - 3 * cnt[3];
		mint res = 1;
		rep(i, cnt[2]) res = res * nokori--;
		res = res * f[nokori / 3];
		if(pe)
			ans = ans - res;
		else
			ans = ans + res;
	}
	cout << ans.v << endl;
}
accuracy: 0.84375
time_ms: 180
memory_kb: 57548
final_rank: 13

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

struct UnionFind {
    vector<int> par;
    vector<int> size;
    UnionFind(int n) {
        par.resize(n);
        size.resize(n,1);
        for(int i = 0; i < n; i++) {
            par[i] = i;
        }
    }
    int find(int x) {
        if(par[x] == x) {
            return x;
        }
        return par[x] = find(par[x]);
    }
    bool same(int x, int y) {
        return find(x) == find(y);
    }
    int consize(int x) {
        return size[find(x)];
    }
    bool unite(int x, int y) {
        x = find(x);
        y = find(y);
        if(x == y) {
            return false;
        }
        if(size[x] > size[y]) {
            swap(x,y);
        }
        par[x] = y;
        size[y] += size[x];
        return true;
    }
};

constexpr int mod = 1000000009;

long long fac[3000005], finv[3000005], inv[3000005];

void COMinit() {
    fac[0] = fac[1] = finv[0] = finv[1] = inv[1] = 1;
    for (int i = 2; i < 3000005; i++) {
        fac[i] = fac[i - 1] * i % mod;
        inv[i] = mod - inv[mod % i] * (mod / i) % mod;
        finv[i] = finv[i - 1] * inv[i] % mod;
    }
}

long long COM(int n, int k){
    if (n < k) return 0;
    if (n < 0 || k < 0) return 0;
    return fac[n] * (finv[k] * finv[n - k] % mod) % mod;
}

long long choose(int n,int k) {
    if(n < 0 || k < 0) return 0;
    if(n == 0) return 1;
    return COM(n+k-1,k-1);
}

int dp[1001001];

void mpl(int &x,int y) {
    x += y;
    if(x >= mod) x -= mod;
}

int main() {
    COMinit();
    int N,M;
    cin >> N >> M;
    dp[0] = 1;
    for(int i = 1; i <= N; i++) {
        dp[i] = COM(3*i-1,2)*dp[i-1]%mod;
    }
    vector<int>A(M),B(M),C(M);
    int cnt = 0;
    map<int,int>mp;
    for(int i = 0; i < M; i++) {
        cin >> A[i] >> B[i] >> C[i];
        A[i]--;
        B[i]--;
        mp[A[i]];
        mp[B[i]];
        if(C[i] == 1) {
            cnt++;
        }
    }
    int res = 0;
    for(auto &i:mp) {
        i.second = res;
        res++;
    }
    int ans = 0;
    for(int i = 0; i < (1 << cnt); i++) {
        int t = 0;
        long long now = 1;
        UnionFind uf(res);
        for(int j = 0; j < M; j++) {
            if(C[j] == 1) {
                if(1 & (i >> t)) {
                    uf.unite(mp[A[j]],mp[B[j]]);
                }
                t++;
            }
            else {
                uf.unite(mp[A[j]],mp[B[j]]);
            }
        }
        int n = N,m = 0;
        for(int j = 0; j < res; j++) {
            if(uf.find(j) == j) {
                if(uf.consize(j) > 3) {
                    now = 0;
                }
                else if(uf.consize(j) == 3) {
                    n--;
                }
                else if(uf.consize(j) == 2) {
                    m++;
                }
            }
        }
        for(int j = 0; j < res; j++) {
            if(uf.find(j) == j) {
                if(uf.consize(j) == 2) {
                    now *= 3*n-2*m;
                    now %= mod;
                    n--;
                    m--;
                }
            }
        }
        now *= dp[n];
        now %= mod;
        if(__builtin_parity(i)) {
            mpl(ans,mod-now);
        }
        else {
            mpl(ans,now);
        }
    }
    cout << ans << endl;
}
accuracy: 1
time_ms: 150
memory_kb: 77668
final_rank: 7

code_snippet: #pragma GCC optimize("Ofast")
#include <iostream>
#include <vector>
#include <algorithm>
#include <map>
#include <queue>
#include <cstdio>
#include <ctime>
#include <assert.h>
#include <chrono>
#include <random>
#include <numeric>
#include <set>
#include <deque>
#include <stack>
#include <sstream>
#include <utility>
#include <cstring>
#include <unordered_map>
#include <unordered_set>
#include <tuple>
#include <array>
#include <bitset>
using namespace std;
typedef long long int ll;
typedef unsigned long long ull;

mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count());
ll myRand(ll B) {
    return (ull)rng() % B;
}
inline double time() {
    return static_cast<long double>(chrono::duration_cast<chrono::nanoseconds>(chrono::steady_clock::now().time_since_epoch()).count()) * 1e-9;
}

struct UnionFind{
    vector<int> par,num;
    UnionFind(int n):par(n),num(n,1){
        iota(par.begin(),par.end(),0);  //include<numeric>
    }
    int find(int v){
        return (par[v]==v)?v:(par[v]=find(par[v]));
    }
    void unite(int u,int v){
        u=find(u),v=find(v);
        if(u==v)return;
        if(num[u]<num[v])swap(u,v);
        num[u]+=num[v];
        par[v]=u;
    }
    bool same(int u,int v){
        return find(u) == find(v);
    }
    int size(int v){
        return num[find(v)];
    }
};

constexpr ll mod = 1e9+9;

ll mod_pow(ll a,ll b){
    a%=mod;
    if(b==0)return 1;
    if(b==1)return a;
    ll res=mod_pow(a,b/2)%mod;
    res*=res; res%=mod;
    if(b%2)res*=a;
    return res%mod;
}

struct perm{
private:
    int sz;
public:
    vector<ll> p,invp;
    perm(int n){
        sz=n+1;
        p.resize(sz),invp.resize(sz);
        p[0]=1;
        for(int i=1;i<=sz-1;i++){
            p[i]=p[i-1]*i%mod;
        }
        invp[sz-1]=mod_pow(p[sz-1],mod-2);
        for(int i=sz-2;i>=0;i--){
            invp[i]=invp[i+1]*(i+1)%mod;
        }
    }
    ll comb(ll x,ll y){
        if(x<y||y<0)return 0;
        return (p[x]*invp[x-y]%mod)*invp[y]%mod;
    }
};
perm p(1<<22);

ull dp[1<<22];

constexpr int inv6 = 833333341;

int main(){
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    {
        dp[0] = 1;
        for(ll i=3;i<(1<<22);i+=3){
            dp[i] = i*(i-1)%mod*(i-2)%mod*inv6%mod*dp[i-3]%mod;
        }
    }
    int n,m; cin >> n >> m;
    int cnt = 0;
    vector<pair<int,pair<int,int>>> v(m);
    vector<int> za;
    for(int i=0;i<m;i++){
        int x,y,z; cin >> x >> y >> z;
        za.push_back(x);
        za.push_back(y);
        cnt += z;
        v[i] = {z,{x,y}};
    }
    sort(za.begin(), za.end());
    za.erase(unique(za.begin(), za.end()), za.end());
    for(int i=0;i<m;i++){
        v[i].second.first = lower_bound(za.begin(), za.end(), v[i].second.first) - za.begin();
        v[i].second.second = lower_bound(za.begin(), za.end(), v[i].second.second) - za.begin();
    }
    int nn = za.size();
    sort(v.rbegin(), v.rend());
    ll res = 0;
    for(int i=0;i<(1<<cnt);i++){
        UnionFind uf(nn);
        for(int j=0;j<m;j++){
            if((1LL<<j)&i) continue;
            uf.unite(v[j].second.first,v[j].second.second);
        }
        int c1 = 3*n-nn;
        int c2 = 0;
        bool ok = true;
        for(int j=0;j<nn;j++){
            if(uf.find(j) == j){
                if(uf.size(j) > 3) ok = false;
                else if(uf.size(j) == 1) c1++;
                else if(uf.size(j) == 2) c2++;
            }
        }
        if(!ok or c2 > c1) continue;
        ll ad = p.p[c1]*p.invp[c1-c2]%mod;
        ad *= dp[c1-c2];
        ad %= mod;
        ad *= p.invp[(c1-c2)/3];
        ad %= mod;
        if(cnt%2 == __builtin_popcount(i)%2){
            res += ad;
        }
        else{
            res -= ad;
        }
    }
    cout << (res%mod+mod)%mod << endl;
}
accuracy: 1
time_ms: 80
memory_kb: 101480
final_rank: 9

code_snippet: #pragma GCC optimize("Ofast")
#include <iostream>
#include <vector>
#include <algorithm>
#include <map>
#include <queue>
#include <cstdio>
#include <ctime>
#include <assert.h>
#include <chrono>
#include <random>
#include <numeric>
#include <set>
#include <deque>
#include <stack>
#include <sstream>
#include <utility>
#include <cstring>
#include <unordered_map>
#include <unordered_set>
#include <tuple>
#include <array>
#include <bitset>
using namespace std;
typedef long long int ll;
typedef unsigned long long ull;

mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count());
ll myRand(ll B) {
    return (ull)rng() % B;
}
inline double time() {
    return static_cast<long double>(chrono::duration_cast<chrono::nanoseconds>(chrono::steady_clock::now().time_since_epoch()).count()) * 1e-9;
}

struct UnionFind{
    vector<int> par,num;
    UnionFind(int n):par(n),num(n,1){
        iota(par.begin(),par.end(),0);  //include<numeric>
    }
    int find(int v){
        return (par[v]==v)?v:(par[v]=find(par[v]));
    }
    void unite(int u,int v){
        u=find(u),v=find(v);
        if(u==v)return;
        if(num[u]<num[v])swap(u,v);
        num[u]+=num[v];
        par[v]=u;
    }
    bool same(int u,int v){
        return find(u) == find(v);
    }
    int size(int v){
        return num[find(v)];
    }
};

constexpr ll mod = 1e9+9;

ll mod_pow(ll a,ll b){
    a%=mod;
    if(b==0)return 1;
    if(b==1)return a;
    ll res=mod_pow(a,b/2)%mod;
    res*=res; res%=mod;
    if(b%2)res*=a;
    return res%mod;
}

struct perm{
private:
    int sz;
public:
    vector<ll> p,invp;
    perm(int n){
        sz=n+1;
        p.resize(sz),invp.resize(sz);
        p[0]=1;
        for(int i=1;i<=sz-1;i++){
            p[i]=p[i-1]*i%mod;
        }
        invp[sz-1]=mod_pow(p[sz-1],mod-2);
        for(int i=sz-2;i>=0;i--){
            invp[i]=invp[i+1]*(i+1)%mod;
        }
    }
    ll comb(ll x,ll y){
        if(x<y||y<0)return 0;
        return (p[x]*invp[x-y]%mod)*invp[y]%mod;
    }
};
perm p(1<<22);

ull dp[1<<22];

constexpr int inv6 = 833333341;

int main(){
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    {
        dp[0] = 1;
        for(ll i=3;i<(1<<22);i+=3){
            dp[i] = i*(i-1)*(i-2)%mod*inv6%mod*dp[i-3]%mod;
        }
    }
    int n,m; cin >> n >> m;
    int cnt = 0;
    vector<pair<int,pair<int,int>>> v(m);
    vector<int> za;
    for(int i=0;i<m;i++){
        int x,y,z; cin >> x >> y >> z;
        za.push_back(x);
        za.push_back(y);
        cnt += z;
        v[i] = {z,{x,y}};
    }
    sort(za.begin(), za.end());
    za.erase(unique(za.begin(), za.end()), za.end());
    for(int i=0;i<m;i++){
        v[i].second.first = lower_bound(za.begin(), za.end(), v[i].second.first) - za.begin();
        v[i].second.second = lower_bound(za.begin(), za.end(), v[i].second.second) - za.begin();
    }
    int nn = za.size();
    sort(v.rbegin(), v.rend());
    ll res = 0;
    for(int i=0;i<(1<<cnt);i++){
        UnionFind uf(nn);
        for(int j=0;j<m;j++){
            if((1LL<<j)&i) continue;
            uf.unite(v[j].second.first,v[j].second.second);
        }
        int c1 = 3*n-nn;
        int c2 = 0;
        bool ok = true;
        for(int j=0;j<nn;j++){
            if(uf.find(j) == j){
                if(uf.size(j) > 3) ok = false;
                else if(uf.size(j) == 1) c1++;
                else if(uf.size(j) == 2) c2++;
            }
        }
        if(!ok or c2 > c1) continue;
        ll ad = p.p[c1]*p.invp[c1-c2]%mod;
        ad *= dp[c1-c2];
        ad %= mod;
        ad *= p.invp[(c1-c2)/3];
        ad %= mod;
        if(cnt%2 == __builtin_popcount(i)%2){
            res += ad;
        }
        else{
            res -= ad;
        }
    }
    cout << (res%mod+mod)%mod << endl;
}
accuracy: 0.375
time_ms: 30
memory_kb: 101472
final_rank: 16

code_snippet: #pragma GCC optimize("Ofast")
#include <iostream>
#include <vector>
#include <algorithm>
#include <map>
#include <queue>
#include <cstdio>
#include <ctime>
#include <assert.h>
#include <chrono>
#include <random>
#include <numeric>
#include <set>
#include <deque>
#include <stack>
#include <sstream>
#include <utility>
#include <cstring>
#include <unordered_map>
#include <unordered_set>
#include <tuple>
#include <array>
#include <bitset>
using namespace std;
typedef long long int ll;
typedef unsigned long long ull;

mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count());
ll myRand(ll B) {
    return (ull)rng() % B;
}
inline double time() {
    return static_cast<long double>(chrono::duration_cast<chrono::nanoseconds>(chrono::steady_clock::now().time_since_epoch()).count()) * 1e-9;
}

struct UnionFind{
    vector<int> par,num;
    UnionFind(int n):par(n),num(n,1){
        iota(par.begin(),par.end(),0);  //include<numeric>
    }
    int find(int v){
        return (par[v]==v)?v:(par[v]=find(par[v]));
    }
    void unite(int u,int v){
        u=find(u),v=find(v);
        if(u==v)return;
        if(num[u]<num[v])swap(u,v);
        num[u]+=num[v];
        par[v]=u;
    }
    bool same(int u,int v){
        return find(u) == find(v);
    }
    int size(int v){
        return num[find(v)];
    }
};

constexpr ll mod = 1e9+9;

ll mod_pow(ll a,ll b){
    a%=mod;
    if(b==0)return 1;
    if(b==1)return a;
    ll res=mod_pow(a,b/2)%mod;
    res*=res; res%=mod;
    if(b%2)res*=a;
    return res%mod;
}

struct perm{
private:
    int sz;
public:
    vector<ll> p,invp;
    perm(int n){
        sz=n+1;
        p.resize(sz),invp.resize(sz);
        p[0]=1;
        for(int i=1;i<=sz-1;i++){
            p[i]=p[i-1]*i%mod;
        }
        invp[sz-1]=mod_pow(p[sz-1],mod-2);
        for(int i=sz-2;i>=0;i--){
            invp[i]=invp[i+1]*(i+1)%mod;
        }
    }
    ll comb(ll x,ll y){
        if(x<y||y<0)return 0;
        return (p[x]*invp[x-y]%mod)*invp[y]%mod;
    }
};
perm p(1<<22);

ll dp[1<<22];

int main(){
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    {
        dp[0] = 1;
        for(ll i=3;i<(1<<22);i+=3){
            dp[i] = i*(i-1)%mod*(i-2)%mod*mod_pow(6,mod-2)%mod*dp[i-3]%mod;
        }
    }
    int n,m; cin >> n >> m;
    int cnt = 0;
    vector<pair<int,pair<int,int>>> v(m);
    vector<int> za;
    for(int i=0;i<m;i++){
        int x,y,z; cin >> x >> y >> z;
        za.push_back(x);
        za.push_back(y);
        cnt += z;
        v[i] = {z,{x,y}};
    }
    sort(za.begin(), za.end());
    za.erase(unique(za.begin(), za.end()), za.end());
    for(int i=0;i<m;i++){
        v[i].second.first = lower_bound(za.begin(), za.end(), v[i].second.first) - za.begin();
        v[i].second.second = lower_bound(za.begin(), za.end(), v[i].second.second) - za.begin();
    }
    int nn = za.size();
    sort(v.rbegin(), v.rend());
    ll res = 0;
    for(int i=0;i<(1<<cnt);i++){
        UnionFind uf(nn);
        for(int j=0;j<m;j++){
            if((1LL<<j)&i) continue;
            uf.unite(v[j].second.first,v[j].second.second);
        }
        int c1 = 3*n-nn;
        int c2 = 0;
        bool ok = true;
        for(int j=0;j<nn;j++){
            if(uf.find(j) == j){
                if(uf.size(j) > 3) ok = false;
                else if(uf.size(j) == 1) c1++;
                else if(uf.size(j) == 2) c2++;
            }
        }
        if(!ok or c2 > c1) continue;
        ll ad = p.p[c1]*p.invp[c1-c2]%mod;
        ad *= dp[c1-c2];
        ad %= mod;
        ad *= p.invp[(c1-c2)/3];
        ad %= mod;
        if(cnt%2 == __builtin_popcount(i)%2){
            res += ad;
        }
        else{
            res -= ad;
        }
    }
    cout << (res%mod+mod)%mod << endl;
}
accuracy: 1
time_ms: 390
memory_kb: 101516
final_rank: 11

code_snippet: #pragma GCC optimize("Ofast")
#include <iostream>
#include <vector>
#include <algorithm>
#include <map>
#include <queue>
#include <cstdio>
#include <ctime>
#include <assert.h>
#include <chrono>
#include <random>
#include <numeric>
#include <set>
#include <deque>
#include <stack>
#include <sstream>
#include <utility>
#include <cstring>
#include <unordered_map>
#include <unordered_set>
#include <tuple>
#include <array>
#include <bitset>
using namespace std;
typedef long long int ll;
typedef unsigned long long ull;

mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count());
ll myRand(ll B) {
    return (ull)rng() % B;
}
inline double time() {
    return static_cast<long double>(chrono::duration_cast<chrono::nanoseconds>(chrono::steady_clock::now().time_since_epoch()).count()) * 1e-9;
}

struct UnionFind{
    vector<int> par,num;
    UnionFind(int n):par(n),num(n,1){
        iota(par.begin(),par.end(),0);  //include<numeric>
    }
    int find(int v){
        return (par[v]==v)?v:(par[v]=find(par[v]));
    }
    void unite(int u,int v){
        u=find(u),v=find(v);
        if(u==v)return;
        if(num[u]<num[v])swap(u,v);
        num[u]+=num[v];
        par[v]=u;
    }
    bool same(int u,int v){
        return find(u) == find(v);
    }
    int size(int v){
        return num[find(v)];
    }
};

constexpr ll mod = 1e9+9;

ll mod_pow(ll a,ll b){
    a%=mod;
    if(b==0)return 1;
    if(b==1)return a;
    ll res=mod_pow(a,b/2)%mod;
    res*=res; res%=mod;
    if(b%2)res*=a;
    return res%mod;
}

struct perm{
private:
    int sz;
public:
    vector<ll> p,invp;
    perm(int n){
        sz=n+1;
        p.resize(sz),invp.resize(sz);
        p[0]=1;
        for(int i=1;i<=sz-1;i++){
            p[i]=p[i-1]*i%mod;
        }
        invp[sz-1]=mod_pow(p[sz-1],mod-2);
        for(int i=sz-2;i>=0;i--){
            invp[i]=invp[i+1]*(i+1)%mod;
        }
    }
    ll comb(ll x,ll y){
        if(x<y||y<0)return 0;
        return (p[x]*invp[x-y]%mod)*invp[y]%mod;
    }
};
perm p(1<<22);

ll dp[1<<22];

int main(){
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    {
        dp[0] = 1;
        for(ll i=3;i<(1<<22);i+=3){
            dp[i] = i*(i-1)*(i-2)/6%mod*dp[i-3]%mod;
        }
    }
    int n,m; cin >> n >> m;
    int cnt = 0;
    vector<pair<int,pair<int,int>>> v(m);
    vector<int> za;
    for(int i=0;i<m;i++){
        int x,y,z; cin >> x >> y >> z;
        za.push_back(x);
        za.push_back(y);
        cnt += z;
        v[i] = {z,{x,y}};
    }
    sort(za.begin(), za.end());
    za.erase(unique(za.begin(), za.end()), za.end());
    for(int i=0;i<m;i++){
        v[i].second.first = lower_bound(za.begin(), za.end(), v[i].second.first) - za.begin();
        v[i].second.second = lower_bound(za.begin(), za.end(), v[i].second.second) - za.begin();
    }
    int nn = za.size();
    sort(v.rbegin(), v.rend());
    ll res = 0;
    for(int i=0;i<(1<<cnt);i++){
        UnionFind uf(nn);
        for(int j=0;j<m;j++){
            if((1LL<<j)&i) continue;
            uf.unite(v[j].second.first,v[j].second.second);
        }
        int c1 = 3*n-nn;
        int c2 = 0;
        bool ok = true;
        for(int j=0;j<nn;j++){
            if(uf.find(j) == j){
                if(uf.size(j) > 3) ok = false;
                else if(uf.size(j) == 1) c1++;
                else if(uf.size(j) == 2) c2++;
            }
        }
        if(!ok or c2 > c1) continue;
        ll ad = p.p[c1]*p.invp[c1-c2]%mod;
        ad *= dp[c1-c2];
        ad %= mod;
        ad *= p.invp[(c1-c2)/3];
        ad %= mod;
        if(cnt%2 == __builtin_popcount(i)%2){
            res += ad;
        }
        else{
            res -= ad;
        }
    }
    cout << (res%mod+mod)%mod << endl;
}
accuracy: 0.375
time_ms: 30
memory_kb: 101472
final_rank: 16

code_snippet: #pragma GCC optimize("Ofast")
#include <iostream>
#include <vector>
#include <algorithm>
#include <map>
#include <queue>
#include <cstdio>
#include <ctime>
#include <assert.h>
#include <chrono>
#include <random>
#include <numeric>
#include <set>
#include <deque>
#include <stack>
#include <sstream>
#include <utility>
#include <cstring>
#include <unordered_map>
#include <unordered_set>
#include <tuple>
#include <array>
#include <bitset>
using namespace std;
typedef long long int ll;
typedef unsigned long long ull;

mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count());
ll myRand(ll B) {
    return (ull)rng() % B;
}
inline double time() {
    return static_cast<long double>(chrono::duration_cast<chrono::nanoseconds>(chrono::steady_clock::now().time_since_epoch()).count()) * 1e-9;
}

struct UnionFind{
    vector<int> par,num;
    UnionFind(int n):par(n),num(n,1){
        iota(par.begin(),par.end(),0);  //include<numeric>
    }
    int find(int v){
        return (par[v]==v)?v:(par[v]=find(par[v]));
    }
    void unite(int u,int v){
        u=find(u),v=find(v);
        if(u==v)return;
        if(num[u]<num[v])swap(u,v);
        num[u]+=num[v];
        par[v]=u;
    }
    bool same(int u,int v){
        return find(u) == find(v);
    }
    int size(int v){
        return num[find(v)];
    }
};

constexpr ll mod = 1e9+9;

ll mod_pow(ll a,ll b){
    a%=mod;
    if(b==0)return 1;
    if(b==1)return a;
    ll res=mod_pow(a,b/2)%mod;
    res*=res; res%=mod;
    if(b%2)res*=a;
    return res%mod;
}

struct perm{
private:
    int sz;
public:
    vector<ll> p,invp;
    perm(int n){
        sz=n+1;
        p.resize(sz),invp.resize(sz);
        p[0]=1;
        for(int i=1;i<=sz-1;i++){
            p[i]=p[i-1]*i%mod;
        }
        invp[sz-1]=mod_pow(p[sz-1],mod-2);
        for(int i=sz-2;i>=0;i--){
            invp[i]=invp[i+1]*(i+1)%mod;
        }
    }
    ll comb(ll x,ll y){
        if(x<y||y<0)return 0;
        return (p[x]*invp[x-y]%mod)*invp[y]%mod;
    }
};
perm p(1<<22);

ll dp[1<<22];

int main(){
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    {
        dp[0] = 1;
        for(ll i=3;i<(1<<22);i+=3){
            dp[i] = i*(i-1)*(i-2)/6%mod*dp[i-3]%mod;
        }
    }
    int n,m; cin >> n >> m;
    int cnt = 0;
    vector<pair<int,pair<int,int>>> v(m);
    vector<int> za;
    for(int i=0;i<m;i++){
        int x,y,z; cin >> x >> y >> z;
        za.push_back(x);
        za.push_back(y);
        cnt += z;
        v[i] = {z,{x,y}};
    }
    sort(za.begin(), za.end());
    za.erase(unique(za.begin(), za.end()), za.end());
    for(int i=0;i<m;i++){
        v[i].second.first = lower_bound(za.begin(), za.end(), v[i].second.first) - za.begin();
        v[i].second.second = lower_bound(za.begin(), za.end(), v[i].second.second) - za.begin();
    }
    int nn = za.size();
    sort(v.rbegin(), v.rend());
    ll res = 0;
    for(int i=0;i<(1<<cnt);i++){
        UnionFind uf(nn);
        for(int j=0;j<m;j++){
            if((1LL<<j)&i) continue;
            uf.unite(v[j].second.first,v[j].second.second);
        }
        int c1 = 3*n-nn;
        int c2 = 0;
        bool ok = true;
        for(int j=0;j<nn;j++){
            if(uf.find(j) == j){
                if(uf.size(j) > 3) ok = false;
                else if(uf.size(j) == 1) c1++;
                else if(uf.size(j) == 2) c2++;
            }
        }
        if(!ok or c2 > c1) continue;
        ll ad = p.p[c1]*p.invp[c1-c2]%mod;
        ad *= dp[c1-c2];
        ad %= mod;
        if(cnt%2 == __builtin_popcount(i)%2){
            res += ad;
        }
        else{
            res -= ad;
        }
    }
    cout << (res%mod+mod)%mod << endl;
}
accuracy: 0.010416666666666666
time_ms: 30
memory_kb: 101276
final_rank: 20

code_snippet: #pragma GCC ta g("avx2")
#pragma GCC optimize("Ofast")
#pragma GCC optimize("unroll-loops")
#include <bits/stdc++.h>
using namespace std;
#define DEBUG
#ifdef DEBUG
template <class T, class U>
ostream &operator<<(ostream &os, const pair<T, U> &p) {
    os << '(' << p.first << ',' << p.second << ')';
    return os;
}
template <class T> ostream &operator<<(ostream &os, const vector<T> &v) {
    os << '{';
    for(int i = 0; i < (int)v.size(); i++) {
        if(i) { os << ','; }
        os << v[i];
    }
    os << '}';
    return os;
}
void debugg() { cerr << endl; }
template <class T, class... Args>
void debugg(const T &x, const Args &... args) {
    cerr << " " << x;
    debugg(args...);
}
#define debug(...)                                                             \
    cerr << __LINE__ << " [" << #__VA_ARGS__ << "]: ", debugg(__VA_ARGS__)
#define dump(x) cerr << __LINE__ << " " << #x << " = " << (x) << endl
#else
#define debug(...) (void(0))
#define dump(x) (void(0))
#endif
using namespace std;
typedef long long ll;
typedef vector<ll> vl;
typedef vector<vl> vvl;
typedef vector<char> vc;
typedef vector<string> vs;
typedef vector<bool> vb;
typedef vector<double> vd;
typedef pair<ll,ll> P;
typedef pair<int,int> pii;
typedef vector<P> vpl;
typedef tuple<ll,ll,ll> tapu;
#define rep(i,n) for(int i=0; i<(n); i++)
#define REP(i,a,b) for(int i=(a); i<(b); i++)
#define all(x) (x).begin(), (x).end()
#define rall(x) (x).rbegin(), (x).rend()
template<typename T1,typename T2> inline bool chmin(T1 &a,T2 b){
	if(a>b){
		a = b; return true;
	}
	else return false;
}
template<typename T1,typename T2> inline bool chmax(T1 &a,T2 b){
	if(a<b){
		a = b; return true;
	}
	else return false;
}
template<typename T> inline void print(T &a){
    int sz = a.size();
    for(auto itr = a.begin(); itr != a.end(); itr++){
		cout << *itr;
        sz--;
        if(sz) cout << " ";
	}
    cout << "\n";
}
template<typename T1,typename T2> inline void print2(T1 a, T2 b){
	cout << a << " " << b << "\n";
}
template<typename T1,typename T2,typename T3> inline void print3(T1 a, T2 b, T3 c){
	cout << a << " " << b << " " << c << "\n";
}
void mark() {cout << "#" << "\n";}
ll pcount(ll x) {return __builtin_popcountll(x);}
//#include <atcoder/maxflow>
//using namespace atcoder;
const int inf = (1<<30)-1;
const ll linf = 1LL<<61;
const int MAX = 5100000;
int dy[8] = {0,1,0,-1,1,-1,-1,1};
int dx[8] = {-1,0,1,0,1,-1,1,-1};
const double pi = acos(-1);
const double eps = 1e-9;
//const int mod = 1e9 + 7;
//const int mod = 998244353;
const int mod = 1e9 + 9;

template <int mod> struct ModInt {
    int x;

    ModInt() : x(0) {}

    ModInt(int64_t y) : x(y >= 0 ? y % mod : (mod - (-y) % mod) % mod) {}

    ModInt &operator+=(const ModInt &p) {
        if((x += p.x) >= mod) x -= mod;
        return *this;
    }

    ModInt &operator-=(const ModInt &p) {
        if((x += mod - p.x) >= mod) x -= mod;
        return *this;
    }

    ModInt &operator*=(const ModInt &p) {
        x = (int)(1LL * x * p.x % mod);
        return *this;
    }

    ModInt &operator/=(const ModInt &p) {
        *this *= p.inverse();
        return *this;
    }

    ModInt operator-() const { return ModInt(-x); }

    ModInt operator+(const ModInt &p) const { return ModInt(*this) += p; }

    ModInt operator-(const ModInt &p) const { return ModInt(*this) -= p; }

    ModInt operator*(const ModInt &p) const { return ModInt(*this) *= p; }

    ModInt operator/(const ModInt &p) const { return ModInt(*this) /= p; }

    bool operator==(const ModInt &p) const { return x == p.x; }

    bool operator!=(const ModInt &p) const { return x != p.x; }

    ModInt inverse() const {
        int a = x, b = mod, u = 1, v = 0, t;
        while(b > 0) {
            t = a / b;
            swap(a -= t * b, b);
            swap(u -= t * v, v);
        }
        return ModInt(u);
    }

    ModInt pow(int64_t n) const {
        ModInt ret(1), mul(x);
        while(n > 0) {
            if(n & 1) ret *= mul;
            mul *= mul;
            n >>= 1;
        }
        return ret;
    }

    friend ostream &operator<<(ostream &os, const ModInt &p) {
        return os << p.x;
    }

    friend istream &operator>>(istream &is, ModInt &a) {
        int64_t t;
        is >> t;
        a = ModInt<mod>(t);
        return (is);
    }

    static int get_mod() { return mod; }
};

using mint = ModInt<mod>;

vector<mint> fac(MAX), finv(MAX), inv(MAX);

void comInit(){
    fac[0] = fac[1] = 1;
    finv[0] = finv[1] = 1;
    inv[1] = 1;
    for(int i=2; i<MAX; i++){
        fac[i] = fac[i-1]*i;
        inv[i] = -inv[mod%i] * (mod/i);
        finv[i] = finv[i-1] * inv[i];
    }
}


mint com(int n, int k){
    if(n < k) return 0;
    if(n < 0 || k < 0) return 0;
    return fac[n] * finv[k] * finv[n-k];
}

struct UnionFind{
    vl p;
    vl rank;
    vl cnt;

    UnionFind(ll n){
        rank.resize(n,0);
        p.resize(n,0);
        cnt.resize(n,0);
        rep(i,n){
            p[i] = i;
            rank[i] = 0;
            cnt[i] = 1;
        }
    }

    ll find(ll x){
        if(x != p[x]) p[x] = find(p[x]);
        return p[x];
    }

    bool same(ll x, ll y){
        return find(x) == find(y);
    }

    void unite(ll x, ll y){
        x = find(x);
        y = find(y);
        if(x == y) return;
        if(rank[x] > rank[y]){
            p[y] = x;
            cnt[x] += cnt[y];
        }else{
            p[x] = y;
            cnt[y] += cnt[x];
            if(rank[x] == rank[y]) rank[y]++;
        }
    }

    ll size(ll x){
        return cnt[find(x)];
    }
};

bool solve(){
    int n,m; cin >> n >> m;
    vl a(m),b(m),c(m);
    rep(i,m) cin >> a[i] >> b[i] >> c[i], a[i]--, b[i]--;
    comInit();
    vl nums;
    rep(i,m){
        nums.push_back(a[i]);
        nums.push_back(b[i]);
    }
    sort(all(nums));
    vl rev(3*n,-1);
    rep(i,nums.size()) rev[nums[i]] = i;
    mint ans = 0;
    vl x,y;
    rep(i,m){
        if(c[i]) x.push_back(a[i]), y.push_back(b[i]);
    }
    int sz = x.size();
    vb vis(nums.size(),false);
    rep(bit,1<<sz){
        UnionFind uf(nums.size());
        rep(i,m){
            if(c[i] == 0) uf.unite(rev[a[i]],rev[b[i]]);
        }
        rep(i,sz){
            if(bit>>i & 1) uf.unite(rev[x[i]],rev[y[i]]);
        }
        rep(i,nums.size()) vis[i] = false;
        int rest = 3*n;
        int twocnt = 0;
        rep(i,nums.size()){
            if(vis[uf.find(i)]) continue;
            vis[uf.find(i)] = true;
            if(uf.size(i) > 3){
                rest = 0;
                break;
            }
            else if(uf.size(i) == 3) rest -= 3;
            else if(uf.size(i) == 2) rest -= 2, twocnt++; 
        }
        if(rest == 0) continue;
        mint res = 1;
        rep(i,twocnt) res *= rest, rest--;
        assert(rest % 3 == 0);
        res *= fac[rest] / mint(6).pow(rest/3) / fac[rest/3];
        if(pcount(bit) & 1) ans -= res;
        else ans += res; 
        //debug(bit,res,rest,twocnt);
    }
    cout << ans << endl;
    return false;
}

int main(){
    while(solve());
}
accuracy: 1
time_ms: 240
memory_kb: 86200
final_rank: 10

code_snippet: #pragma GCC ta g("avx2")
#pragma GCC optimize("Ofast")
#pragma GCC optimize("unroll-loops")
#include <bits/stdc++.h>
using namespace std;
#define DEBUG
#ifdef DEBUG
template <class T, class U>
ostream &operator<<(ostream &os, const pair<T, U> &p) {
    os << '(' << p.first << ',' << p.second << ')';
    return os;
}
template <class T> ostream &operator<<(ostream &os, const vector<T> &v) {
    os << '{';
    for(int i = 0; i < (int)v.size(); i++) {
        if(i) { os << ','; }
        os << v[i];
    }
    os << '}';
    return os;
}
void debugg() { cerr << endl; }
template <class T, class... Args>
void debugg(const T &x, const Args &... args) {
    cerr << " " << x;
    debugg(args...);
}
#define debug(...)                                                             \
    cerr << __LINE__ << " [" << #__VA_ARGS__ << "]: ", debugg(__VA_ARGS__)
#define dump(x) cerr << __LINE__ << " " << #x << " = " << (x) << endl
#else
#define debug(...) (void(0))
#define dump(x) (void(0))
#endif
using namespace std;
typedef long long ll;
typedef vector<ll> vl;
typedef vector<vl> vvl;
typedef vector<char> vc;
typedef vector<string> vs;
typedef vector<bool> vb;
typedef vector<double> vd;
typedef pair<ll,ll> P;
typedef pair<int,int> pii;
typedef vector<P> vpl;
typedef tuple<ll,ll,ll> tapu;
#define rep(i,n) for(int i=0; i<(n); i++)
#define REP(i,a,b) for(int i=(a); i<(b); i++)
#define all(x) (x).begin(), (x).end()
#define rall(x) (x).rbegin(), (x).rend()
template<typename T1,typename T2> inline bool chmin(T1 &a,T2 b){
	if(a>b){
		a = b; return true;
	}
	else return false;
}
template<typename T1,typename T2> inline bool chmax(T1 &a,T2 b){
	if(a<b){
		a = b; return true;
	}
	else return false;
}
template<typename T> inline void print(T &a){
    int sz = a.size();
    for(auto itr = a.begin(); itr != a.end(); itr++){
		cout << *itr;
        sz--;
        if(sz) cout << " ";
	}
    cout << "\n";
}
template<typename T1,typename T2> inline void print2(T1 a, T2 b){
	cout << a << " " << b << "\n";
}
template<typename T1,typename T2,typename T3> inline void print3(T1 a, T2 b, T3 c){
	cout << a << " " << b << " " << c << "\n";
}
void mark() {cout << "#" << "\n";}
ll pcount(ll x) {return __builtin_popcountll(x);}
//#include <atcoder/maxflow>
//using namespace atcoder;
const int inf = (1<<30)-1;
const ll linf = 1LL<<61;
const int MAX = 5100000;
int dy[8] = {0,1,0,-1,1,-1,-1,1};
int dx[8] = {-1,0,1,0,1,-1,1,-1};
const double pi = acos(-1);
const double eps = 1e-9;
//const int mod = 1e9 + 7;
//const int mod = 998244353;
const int mod = 1e9 + 9;

template <int mod> struct ModInt {
    int x;

    ModInt() : x(0) {}

    ModInt(int64_t y) : x(y >= 0 ? y % mod : (mod - (-y) % mod) % mod) {}

    ModInt &operator+=(const ModInt &p) {
        if((x += p.x) >= mod) x -= mod;
        return *this;
    }

    ModInt &operator-=(const ModInt &p) {
        if((x += mod - p.x) >= mod) x -= mod;
        return *this;
    }

    ModInt &operator*=(const ModInt &p) {
        x = (int)(1LL * x * p.x % mod);
        return *this;
    }

    ModInt &operator/=(const ModInt &p) {
        *this *= p.inverse();
        return *this;
    }

    ModInt operator-() const { return ModInt(-x); }

    ModInt operator+(const ModInt &p) const { return ModInt(*this) += p; }

    ModInt operator-(const ModInt &p) const { return ModInt(*this) -= p; }

    ModInt operator*(const ModInt &p) const { return ModInt(*this) *= p; }

    ModInt operator/(const ModInt &p) const { return ModInt(*this) /= p; }

    bool operator==(const ModInt &p) const { return x == p.x; }

    bool operator!=(const ModInt &p) const { return x != p.x; }

    ModInt inverse() const {
        int a = x, b = mod, u = 1, v = 0, t;
        while(b > 0) {
            t = a / b;
            swap(a -= t * b, b);
            swap(u -= t * v, v);
        }
        return ModInt(u);
    }

    ModInt pow(int64_t n) const {
        ModInt ret(1), mul(x);
        while(n > 0) {
            if(n & 1) ret *= mul;
            mul *= mul;
            n >>= 1;
        }
        return ret;
    }

    friend ostream &operator<<(ostream &os, const ModInt &p) {
        return os << p.x;
    }

    friend istream &operator>>(istream &is, ModInt &a) {
        int64_t t;
        is >> t;
        a = ModInt<mod>(t);
        return (is);
    }

    static int get_mod() { return mod; }
};

using mint = ModInt<mod>;

vector<mint> fac(MAX), finv(MAX), inv(MAX);

void comInit(){
    fac[0] = fac[1] = 1;
    finv[0] = finv[1] = 1;
    inv[1] = 1;
    for(int i=2; i<MAX; i++){
        fac[i] = fac[i-1]*i;
        inv[i] = -inv[mod%i] * (mod/i);
        finv[i] = finv[i-1] * inv[i];
    }
}


mint com(int n, int k){
    if(n < k) return 0;
    if(n < 0 || k < 0) return 0;
    return fac[n] * finv[k] * finv[n-k];
}

struct UnionFind{
    vl p;
    vl rank;
    vl cnt;

    UnionFind(ll n){
        rank.resize(n,0);
        p.resize(n,0);
        cnt.resize(n,0);
        rep(i,n){
            p[i] = i;
            rank[i] = 0;
            cnt[i] = 1;
        }
    }

    ll find(ll x){
        if(x != p[x]) p[x] = find(p[x]);
        return p[x];
    }

    bool same(ll x, ll y){
        return find(x) == find(y);
    }

    void unite(ll x, ll y){
        x = find(x);
        y = find(y);
        if(x == y) return;
        if(rank[x] > rank[y]){
            p[y] = x;
            cnt[x] += cnt[y];
        }else{
            p[x] = y;
            cnt[y] += cnt[x];
            if(rank[x] == rank[y]) rank[y]++;
        }
    }

    ll size(ll x){
        return cnt[find(x)];
    }
};

bool solve(){
    int n,m; cin >> n >> m;
    vl a(m),b(m),c(m);
    rep(i,m) cin >> a[i] >> b[i] >> c[i], a[i]--, b[i]--;
    comInit();
    vl nums;
    rep(i,m){
        nums.push_back(a[i]);
        nums.push_back(b[i]);
    }
    sort(all(nums));
    vl rev(3*n,-1);
    rep(i,nums.size()) rev[nums[i]] = i;
    mint ans = 0;
    vl x,y;
    rep(i,m){
        if(c[i]) x.push_back(a[i]), y.push_back(b[i]);
    }
    int sz = x.size();
    vb vis(nums.size(),false);
    rep(bit,1<<sz){
        UnionFind uf(nums.size());
        rep(i,m){
            if(c[i] == 0) uf.unite(rev[a[i]],rev[b[i]]);
        }
        rep(i,sz){
            if(bit>>i & 1) uf.unite(rev[x[i]],rev[y[i]]);
        }
        rep(i,nums.size()) vis[i] = false;
        int rest = 3*n;
        int twocnt = 0;
        rep(i,nums.size()){
            if(vis[uf.find(i)]) continue;
            vis[uf.find(i)] = true;
            if(uf.size(i) > 3) rest = 0;
            else if(uf.size(i) == 3) rest -= 3;
            else if(uf.size(i) == 2) rest -= 2, twocnt++; 
        }
        mint res = 1;
        rep(i,twocnt) res *= rest, rest--;
        assert(rest % 3 == 0);
        res *= fac[rest] / mint(6).pow(rest/3);
        if(pcount(bit) & 1) ans -= res;
        else ans += res; 
        //debug(bit,res,rest,twocnt);
    }
    cout << ans << endl;
    return false;
}

int main(){
    while(solve());
}
accuracy: 0.010416666666666666
time_ms: 70
memory_kb: 62544
final_rank: 19

code_snippet: #line 1 "a.cpp"
/*	author:  Kite_kuma
	created: 2022.05.10 20:37:00 */

#line 1 "SPJ-Library/data_structure/union_find.hpp"
#include <vector>

struct disjoint_set_union {
   private:
	std::vector<int> data;	// data[root] = -size, data[not_root] = parent
	int cpnt;				// number of components

   public:
	disjoint_set_union(int N) : data(N, -1), cpnt(N) {}

	int components() const { return cpnt; }

	int root(int x) {
		if(data[x] < 0) return x;
		return data[x] = root(data[x]);
	}

	bool same(int x, int y) { return root(x) == root(y); }

	bool unite(int x, int y) {	// merge x and y
		x = root(x);
		y = root(y);
		if(x == y) return false;
		--cpnt;
		if(data[x] > data[y]) std::swap(x, y);	// balancing
		data[x] += data[y];
		data[y] = x;
		return true;
	}

	int size(int x) { return -data[root(x)]; }

	std::vector<std::vector<int>> groups() {
		std::vector<std::vector<int>> g(data.size()), ret;
		for(int i = 0; i < (int)data.size(); i++) g[root(i)].push_back((int)i);
		for(const std::vector<int> &vec : g)
			if(!vec.empty()) ret.push_back(vec);
		return ret;
	}
};
#line 2 "SPJ-Library/template/kuma.hpp"

#line 2 "SPJ-Library/template/basic_func.hpp"

#include <algorithm>
#include <cassert>
#include <iostream>
#line 7 "SPJ-Library/template/basic_func.hpp"

template <class T0, class T1, class T2>
inline bool in_range(T0 x, T1 lef, T2 rig) {
	return ((lef <= x) && (x < rig));
}

template <class T>
inline bool chmin(T &a, T b) {
	if(a > b) {
		a = b;
		return true;
	}
	return false;
}
template <class T>
inline bool chmax(T &a, T b) {
	if(a < b) {
		a = b;
		return true;
	}
	return false;
}

void Yes(bool flag = true) { std::cout << (flag ? "Yes" : "No") << '\n'; }
void YES(bool flag = true) { std::cout << (flag ? "YES" : "NO") << '\n'; }

template <class T>
void drop(T answer) {
	std::cout << answer << '\n';
	exit(0);
}

template <class T>
void vout(std::vector<T> const &v, bool vertically = 0) {
	if(vertically) {
		for(auto const &a : v) {
			std::cout << a << '\n';
		}
		return;
	}
	for(auto it = v.begin(); it != v.end(); it++) {
		std::cout << (*it);
		if(it != v.end() - 1) {
			std::cout << ' ';
		}
	}
	std::cout << '\n';
	return;
}

inline void print() { std::cout << '\n'; }
template <class T>
inline void print(const T &x) {
	std::cout << x << '\n';
	return;
}
template <typename Head, typename... Tail>
void print(const Head &H, const Tail &... T) {
	std::cout << H << " ";
	print(T...);
}

template <class T>
void add(std::vector<T> &v, T value) {
	for(auto &a : v) a += value;
	return;
}

template <class T>
T greatest_lower_multiple(T x, T d) {
	if(d == 0) return 0;
	if(d < 0) d *= -1;
	T y = x % d;
	if(y < 0) y += d;
	return x - y;
}

template <class T>
T least_upper_multiple(T x, T d) {
	return -greatest_lower_multiple(-x, d);
}

// ceil(a / b);
template <class T>
T dup(T a, T b) {
	assert(b);
	if(b < 0) {
		a *= -1;
		b *= -1;
	}
	return least_upper_multiple(a, b) / b;
}

long long pow_ll(long long a, long long n) {
	if(n == 0) return 1LL;
	if(a == 0) return 0LL;
	if(a == 1) return 1LL;
	if(a == -1) return (n & 1LL) ? -1LL : 1LL;
	long long res = 1;
	while(n > 0) {
		if(n & 1) res *= a;
		a *= a;
		n >>= 1;
	}
	return res;
}

long long modpow(long long a, long long n, long long mod) {	 // a^n mod
	assert(n >= 0 && mod);
	if(mod == 1) return 0LL;
	long long res = 1;
	a %= mod;
	while(n > 0) {
		if(n & 1) res = res * a % mod;
		a = a * a % mod;
		n >>= 1;
	}
	return res < 0 ? res + mod : res;
}

// return x which satisfies a * x % mod == gcd(a, mod)
// not (mod | a)
long long modinv(long long a, const long long mod) {
	long long b = mod, u = 1, v = 0;
	while(b) {
		long long t = a / b;
		a -= t * b;
		std::swap(a, b);
		u -= t * v;
		std::swap(u, v);
	}
	u %= mod;
	if(u < 0) u += mod;
	return u;
}

template <class T>
int lb(const std::vector<T> &a, const T x) {
	return (int)std::distance(a.begin(), std::lower_bound(a.begin(), a.end(), x));
}
template <class T>
int ub(const std::vector<T> &a, const T x) {
	return (int)std::distance(a.begin(), std::upper_bound(a.begin(), a.end(), x));
}
template <class T>
void unq_sort(std::vector<T> &a) {
	std::sort(a.begin(), a.end());
	a.erase(std::unique(a.begin(), a.end()), a.end());
}
template <class T>
std::vector<int> press(const std::vector<T> &a) {
	std::vector<T> vec = a;
	unq_sort(vec);
	std::vector<int> ret;
	for(const auto &v : a) ret.push_back(lb(vec, v));
	return ret;
}
#line 1 "SPJ-Library/template/header.hpp"
#include <bits/stdc++.h>
#line 2 "SPJ-Library/template/io.hpp"

#line 4 "SPJ-Library/template/io.hpp"

#line 8 "SPJ-Library/template/debug.hpp"

#line 3 "SPJ-Library/template/constants.hpp"

constexpr int inf = 1000'000'000;
constexpr long long INF = 1'000'000'000'000'000'000LL;
constexpr int mod_1000000007 = 1000000007;
constexpr int mod_998244353 = 998244353;
const long double pi = acosl(-1.);
constexpr int dx[8] = {1, 0, -1, 0, 1, -1, -1, 1};
constexpr int dy[8] = {0, 1, 0, -1, 1, 1, -1, -1};
#line 10 "SPJ-Library/template/debug.hpp"

namespace viewer {

template <typename T, typename U>
void view(const std::pair<T, U> &p);

template <class T0, class T1, class T2>
void view(const std::tuple<T0, T1, T2> &p);

template <class T0, class T1, class T2, class T3>
void view(const std::tuple<T0, T1, T2, T3> &p);

void view(const long long &e);

void view(const int &e);

template <typename T>
void view(const T &e);

template <typename T>
void view(const std::set<T> &s);

template <typename T>
void view(const std::unordered_set<T> &s);

template <typename T>
void view(const std::multiset<T> &s);

template <typename T>
void view(const std::unordered_multiset<T> &s);

template <typename T>
void view(const std::vector<T> &v);

template <typename T, std::size_t ary_size>
void view(const std::array<T, ary_size> &v);

template <typename T>
void view(const std::vector<std::vector<T>> &vv);

template <typename T, typename U>
void view(const std::vector<std::pair<T, U>> &v);

template <class T0, class T1, class T2>
void view(const std::vector<std::tuple<T0, T1, T2>> &v);

template <class T0, class T1, class T2, class T3>
void view(const std::vector<std::tuple<T0, T1, T2, T3>> &v);

template <typename map_type>
void view_map_container(const map_type &m);

template <typename T, typename U>
void view(const std::map<T, U> &m);

template <typename T, typename U>
void view(const std::unordered_map<T, U> &m);

template <typename container_type>
void view_container(const container_type &c, bool vertically = false) {
	typename container_type::const_iterator begin = c.begin();
	const typename container_type::const_iterator end = c.end();
	if(vertically) {
		std::cerr << "{\n";
		while(begin != end) {
			std::cerr << '\t';
			view(*(begin++));
			if(begin != end) std::cerr << ',';
			std::cerr << '\n';
		}
		std::cerr << '}';
		return;
	}
	std::cerr << "{ ";
	while(begin != end) {
		view(*(begin++));
		if(begin != end) std::cerr << ',';
		std::cerr << ' ';
	}
	std::cerr << '}';
}

template <typename T, typename U>
void view(const std::pair<T, U> &p) {
	std::cerr << '(';
	view(p.first);
	std::cerr << ", ";
	view(p.second);
	std::cerr << ')';
}

template <class T0, class T1, class T2>
void view(const std::tuple<T0, T1, T2> &p) {
	std::cerr << '(';
	view(std::get<0>(p));
	std::cerr << ", ";
	view(std::get<1>(p));
	std::cerr << ", ";
	view(std::get<2>(p));
	std::cerr << ')';
}

template <class T0, class T1, class T2, class T3>
void view(const std::tuple<T0, T1, T2, T3> &p) {
	std::cerr << '(';
	view(std::get<0>(p));
	std::cerr << ", ";
	view(std::get<1>(p));
	std::cerr << ", ";
	view(std::get<2>(p));
	std::cerr << ", ";
	view(std::get<3>(p));
	std::cerr << ')';
}

void view(const long long &e) {
	if(e == INF)
		std::cerr << "INF";
	else if(e == -INF)
		std::cerr << "-INF";
	else
		std::cerr << e;
}

void view(const int &e) {
	if(e == inf)
		std::cerr << "inf";
	else if(e == -inf)
		std::cerr << "-inf";
	else
		std::cerr << e;
}

template <typename T>
void view(const T &e) {
	std::cerr << e;
}

template <typename T>
void view(const std::set<T> &s) {
	view_container(s);
}

template <typename T>
void view(const std::unordered_set<T> &s) {
	view_container(s);
}

template <typename T>
void view(const std::multiset<T> &s) {
	view_container(s);
}

template <typename T>
void view(const std::unordered_multiset<T> &s) {
	view_container(s);
}

template <typename T>
void view(const std::vector<T> &v) {
	view_container(v);
}

template <typename T, std::size_t ary_size>
void view(const std::array<T, ary_size> &v) {
	view_container(v);
}

template <typename T>
void view(const std::vector<std::vector<T>> &vv) {
	view_container(vv, true);
}

template <typename T, typename U>
void view(const std::vector<std::pair<T, U>> &v) {
	view_container(v, true);
}

template <class T0, class T1, class T2>
void view(const std::vector<std::tuple<T0, T1, T2>> &v) {
	view_container(v, true);
}

template <class T0, class T1, class T2, class T3>
void view(const std::vector<std::tuple<T0, T1, T2, T3>> &v) {
	view_container(v, true);
}

template <typename map_type>
void view_map_container(const map_type &m) {
	std::cerr << "{\n";
	for(typename map_type::const_iterator it = m.begin(); it != m.end(); it++) {
		std::cerr << "\t[";
		view(it->first);
		std::cerr << "] : ";
		view(it->second);
		std::cerr << '\n';
	}
	std::cerr << "}";
}

template <typename T, typename U>
void view(const std::map<T, U> &m) {
	view_map_container(m);
}

template <typename T, typename U>
void view(const std::unordered_map<T, U> &m) {
	view_map_container(m);
}

}  // namespace viewer

// when compiling : g++ foo.cpp -DLOCAL
#ifdef LOCAL
void debug_out() {}
template <typename T>
void debug_out(const T &x) {
	viewer::view(x);
}
template <typename Head, typename... Tail>
void debug_out(const Head &H, const Tail &... T) {
	viewer::view(H);
	std::cerr << ", ";
	debug_out(T...);
}
#define debug(...)                                                \
	do {                                                          \
		std::cerr << __LINE__ << " [" << #__VA_ARGS__ << "] : ["; \
		debug_out(__VA_ARGS__);                                   \
		std::cerr << "]\n";                                       \
	} while(0)
#else
#define debug(...) (void(0))
#endif
#line 2 "SPJ-Library/template/scanner.hpp"

#line 6 "SPJ-Library/template/scanner.hpp"

namespace scanner {
template <class T>
void scan(T &a) {
	std::cin >> a;
}

template <class T, class L>
void scan(std::pair<T, L> &p) {
	scan(p.first);
	scan(p.second);
}

template <class T0, class T1, class T2>
void scan(std::tuple<T0, T1, T2> &p) {
	T0 t0;
	T1 t1;
	T2 t2;
	scan(t0);
	scan(t1);
	scan(t2);
	p = std::make_tuple(t0, t1, t2);
}

template <class T0, class T1, class T2, class T3>
void scan(std::tuple<T0, T1, T2, T3> &p) {
	T0 t0;
	T1 t1;
	T2 t2;
	T3 t3;
	scan(t0);
	scan(t1);
	scan(t2);
	scan(t3);
	p = std::make_tuple(t0, t1, t2, t3);
}

template <class T>
void scan(std::vector<T> &a) {
	for(auto &i : a) scan(i);
}

void INPUT() {}
template <class Head, class... Tail>
void INPUT(Head &head, Tail &... tail) {
	scan(head);
	INPUT(tail...);
}
}  // namespace scanner
#line 7 "SPJ-Library/template/io.hpp"

#define VEC(type, name, size) \
	vector<type> name(size);  \
	scanner::INPUT(name)
#define VVEC(type, name, h, w)                     \
	vector<vector<type>> name(h, vector<type>(w)); \
	scanner::INPUT(name)
#define INT(...)     \
	int __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define LL(...)            \
	long long __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define STR(...)        \
	string __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define CHR(...)      \
	char __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define DBL(...)        \
	double __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define LD(...)              \
	long double __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define TPL3(type0, type1, type2, name)   \
	std::tuple<type0, type1, type2> name; \
	scanner::INPUT(name);
#define TPL4(type0, type1, type2, type3, name)   \
	std::tuple<type0, type1, type2, type3> name; \
	scanner::INPUT(name);

template <typename T1, typename T2>
std::istream &operator>>(std::istream &is, std::pair<T1, T2> &p) {
	is >> p.first >> p.second;
	return is;
}

template <typename T1, typename T2>
std::ostream &std::operator<<(std::ostream &os, const std::pair<T1, T2> &p) {
	os << p.first << ' ' << p.second;
	return os;
}
template <class T>
std::ostream &std::operator<<(std::ostream &os, const std::vector<T> &v) {
	for(typename std::vector<T>::const_iterator it = v.begin(); it != v.end(); it++) {
		if(it != v.begin()) std::cerr << ' ';
		os << *it;
	}
	return os;
}

struct fast_io {
	fast_io() {
		std::ios::sync_with_stdio(false);
		std::cin.tie(nullptr);
		std::cout << std::fixed << std::setprecision(15);
		srand((unsigned)time(NULL));
	}
} fast_io_;
#line 2 "SPJ-Library/template/macros.hpp"

#define foa(s, v) for(auto &s : v)
#define all(v) (v).begin(), (v).end()
#define rall(v) (v).rbegin(), (v).rend()
#define pcnt(n) __builtin_popcountll((long long)n)

#define REPname(a, b, c, d, e, ...) e
#define rep(...) REPname(__VA_ARGS__, REP3, REP2, REP1, REP0)(__VA_ARGS__)
#define REP0(x) for(int Counter_in_rep_macro = 0; Counter_in_rep_macro < (x); ++Counter_in_rep_macro)
#define REP1(i, x) for(int i = 0; i < (x); ++i)
#define REP2(i, l, r) for(int i = (l); i < (r); ++i)
#define REP3(i, l, r, c) for(int i = (l); i < (r); i += (c))
#define DREPname(a, b, c, d, e, ...) e
#define drep(...) DREPname(__VA_ARGS__, DREP3, DREP2, DREP1)(__VA_ARGS__)
#define DREP1(i, x) for(int i = (x)-1; i >= 0; --i)
#define DREP2(i, l, r) for(int i = (r)-1; i >= (l); --i)
#define DREP3(i, l, r, c) for(int i = (r)-1; i >= (l); i -= (c))

using ll = long long;
using ld = long double;
using ull = unsigned long long;
using vi = std::vector<int>;
using vvi = std::vector<std::vector<int>>;
using vvvi = std::vector<std::vector<std::vector<int>>>;
using vll = std::vector<ll>;
using vvll = std::vector<vll>;
using vvvll = std::vector<vvll>;
template <class T = ll>
using V = std::vector<T>;
template <class T = ll>
using VV = V<V<T>>;
template <class T = ll>
using VVV = V<V<V<T>>>;
template <class T = ll>
using pqup = std::priority_queue<T, std::vector<T>, std::greater<T>>;
template <class T = ll>
using pqdn = std::priority_queue<T>;
#line 7 "SPJ-Library/template/kuma.hpp"

using namespace std;
#line 6 "a.cpp"

#pragma region modint

const int mod = mod_1000000007 + 2;

template <int mod>
struct ModInt {
	int x;
	ModInt() : x(0) {}
	ModInt(long long y) : x(y >= 0 ? y % mod : (mod - (-y) % mod) % mod) {}
	ModInt &operator+=(const ModInt &p) {
		if((x += p.x) >= mod) x -= mod;
		return *this;
	}
	ModInt &operator-=(const ModInt &p) {
		if((x += mod - p.x) >= mod) x -= mod;
		return *this;
	}
	ModInt &operator*=(const ModInt &p) {
		x = (int)(1LL * x * p.x % mod);
		return *this;
	}
	ModInt &operator/=(const ModInt &p) {
		*this *= p.inverse();
		return *this;
	}
	ModInt operator-() const { return ModInt(-x); }
	ModInt operator+(const ModInt &p) const { return ModInt(*this) += p; }
	ModInt operator-(const ModInt &p) const { return ModInt(*this) -= p; }
	ModInt operator*(const ModInt &p) const { return ModInt(*this) *= p; }
	ModInt operator/(const ModInt &p) const { return ModInt(*this) /= p; }
	bool operator==(const ModInt &p) const { return x == p.x; }
	bool operator!=(const ModInt &p) const { return x != p.x; }
	ModInt inverse() const {
		int a = x, b = mod, u = 1, v = 0, t;
		while(b > 0) {
			t = a / b;
			swap(a -= t * b, b);
			swap(u -= t * v, v);
		}
		return ModInt(u);
	}
	ModInt pow(long long n) const {
		ModInt ret(1), mul(x);
		while(n > 0) {
			if(n & 1) ret *= mul;
			mul *= mul;
			n >>= 1;
		}
		return ret;
	}
	friend ostream &operator<<(ostream &os, const ModInt &p) { return os << p.x; }
	friend istream &operator>>(istream &is, ModInt &a) {
		long long t;
		is >> t;
		a = ModInt<mod>(t);
		return (is);
	}
	static int get_mod() { return mod; }
};
using mint = ModInt<mod>;

#pragma endregion

#pragma region binomial_coefficient

// normal binomial coefficient
vector<vector<long long>> binom_memo(100, vector<long long>(100, -1));
long long binom(int n, int k) {
	assert(n < 100 && k < 100);
	if(n < k || k < 0 || n < 0) return 0;
	if(binom_memo[n][k] != -1) return binom_memo[n][k];
	long long res;
	if(n - k < k)
		res = binom(n, n - k);
	else if(k == 0)
		res = 1;
	else
		res = binom(n - 1, k - 1) + binom(n - 1, k);
	binom_memo[n][k] = res;
	return res;
}

vector<long long> fac, finv, inv;
struct mod_factorial {
	const int MAX;	// time: 300ms, when MAX = 3*10^7, time: 1900ms
	mod_factorial(const int MAX_ = 5100000) : MAX(MAX_) {
		fac.resize(MAX), finv.resize(MAX), inv.resize(MAX);
		fac[0] = fac[1] = 1;
		finv[0] = finv[1] = 1;
		inv[1] = 1;
		for(int i = 2; i < MAX; i++) {
			fac[i] = fac[i - 1] * i % mod;
			inv[i] = mod - inv[mod % i] * (mod / i) % mod;
			finv[i] = finv[i - 1] * inv[i] % mod;
		}
	}
} mod_factorial_;

long long com_mod(long long n, long long k) {
	if(n < k || n < 0 || k < 0) return 0;
	return fac[n] * (finv[k] * finv[n - k] % mod) % mod;
}

long long permutation_mod(long long n, long long k) {
	if(n < k || n < 0 || k < 0) return 0;
	return fac[n] * finv[n - k] % mod;
}

// patterns of n of undistinguished balls in k of distinguished boxes
long long multi_choose_mod(long long n, long long k) { return com_mod(n + k - 1, n); }

#pragma endregion

int main() {
	int n;
	cin >> n;
	int m;
	cin >> m;
	vector<pair<int, int>> good, bad;
	map<int, int> id;
	rep(i, m) {
		int a, b;
		cin >> a >> b;
		--a;
		--b;
		id[a] = 0;
		id[b] = 0;
		int c;
		cin >> c;
		(c ? bad : good).emplace_back(a, b);
	}

	// int sz = (int)id.size();
	int sz = 0;
	foa(t, id) t.second = sz++;
	foa(t, good) {
		t.first = id[t.first];
		t.second = id[t.second];
	}
	foa(t, bad) {
		t.first = id[t.first];
		t.second = id[t.second];
	}

	debug(good);
	debug(bad);

	disjoint_set_union dsu(sz);

	foa(t, good) { dsu.unite(t.first, t.second); }

	disjoint_set_union dsu_tmp = dsu;

	V<mint> make_triple = {1};
	rep(i, n) {
		const mint nxt = i * 3 + 2;
		make_triple.push_back(make_triple.back() * (nxt - 1) * nxt / 2);
	}

	debug(make_triple);
	auto calc = [&]() {
		int two, three;
		two = three = 0;
		foa(g, dsu_tmp.groups()) {
			int gs = int(g.size());
			if(gs > 3) return mint(0);
			if(gs == 3) three++;
			if(gs == 2) two++;
		}
		int one = n * 3 - three * 3 - two * 2;
		debug(one, two, three);
		if(one < two) return mint(0);
		mint res = permutation_mod(one, two);
		debug(res);
		debug(make_triple.at(n - three));
		return res * make_triple.at((one - two) / 3);
	};
	const int bs = int(bad.size());
	mint ans = 0;
	rep(error_bits, 1 << bs) {
		dsu_tmp = dsu;
		rep(j, bs) {
			if((error_bits >> j) & 1) {
				auto &t = bad[j];
				dsu_tmp.unite(t.first, t.second);
			}
		}
		mint ad = calc();
		debug(error_bits, ad);
		if(pcnt(error_bits) & 1) ad = -ad;
		ans += ad;
	}

	drop(ans);

	return 0;
}
accuracy: 1
time_ms: 510
memory_kb: 128960
final_rank: 12

code_snippet: #include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<class T>bool chmax(T &a, const T &b) { if (a<b) { a=b; return true; } return false; }
template<class T>bool chmin(T &a, const T &b) { if (b<a) { a=b; return true; } return false; }
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
#define mp make_pair
#define si(x) int(x.size())
const int mod=1000000007,MAX=3000005,INF=1<<30;

//modintのみ

// from: https://gist.github.com/yosupo06/ddd51afb727600fd95d9d8ad6c3c80c9
// (based on AtCoder STL)

#include <cassert>
#include <numeric>
#include <type_traits>

namespace atcoder {
    
    namespace internal {
        
#ifndef _MSC_VER
        template <class T>
        using is_signed_int128 =
        typename std::conditional<std::is_same<T, __int128_t>::value ||
        std::is_same<T, __int128>::value,
        std::true_type,
        std::false_type>::type;
        
        template <class T>
        using is_unsigned_int128 =
        typename std::conditional<std::is_same<T, __uint128_t>::value ||
        std::is_same<T, unsigned __int128>::value,
        std::true_type,
        std::false_type>::type;
        
        template <class T>
        using make_unsigned_int128 =
        typename std::conditional<std::is_same<T, __int128_t>::value,
        __uint128_t,
        unsigned __int128>;
        
        template <class T>
        using is_integral = typename std::conditional<std::is_integral<T>::value ||
        is_signed_int128<T>::value ||
        is_unsigned_int128<T>::value,
        std::true_type,
        std::false_type>::type;
        
        template <class T>
        using is_signed_int = typename std::conditional<(is_integral<T>::value &&
                                                         std::is_signed<T>::value) ||
        is_signed_int128<T>::value,
        std::true_type,
        std::false_type>::type;
        
        template <class T>
        using is_unsigned_int =
        typename std::conditional<(is_integral<T>::value &&
                                   std::is_unsigned<T>::value) ||
        is_unsigned_int128<T>::value,
        std::true_type,
        std::false_type>::type;
        
        template <class T>
        using to_unsigned = typename std::conditional<
        is_signed_int128<T>::value,
        make_unsigned_int128<T>,
        typename std::conditional<std::is_signed<T>::value,
        std::make_unsigned<T>,
        std::common_type<T>>::type>::type;
        
#else
        
        template <class T> using is_integral = typename std::is_integral<T>;
        
        template <class T>
        using is_signed_int =
        typename std::conditional<is_integral<T>::value && std::is_signed<T>::value,
        std::true_type,
        std::false_type>::type;
        
        template <class T>
        using is_unsigned_int =
        typename std::conditional<is_integral<T>::value &&
        std::is_unsigned<T>::value,
        std::true_type,
        std::false_type>::type;
        
        template <class T>
        using to_unsigned = typename std::conditional<is_signed_int<T>::value,
        std::make_unsigned<T>,
        std::common_type<T>>::type;
        
#endif
        
        template <class T>
        using is_signed_int_t = std::enable_if_t<is_signed_int<T>::value>;
        
        template <class T>
        using is_unsigned_int_t = std::enable_if_t<is_unsigned_int<T>::value>;
        
        template <class T> using to_unsigned_t = typename to_unsigned<T>::type;
        
    }  // namespace internal
    
}  // namespace atcoder

#include <utility>

namespace atcoder {
    
    namespace internal {
        
        constexpr long long safe_mod(long long x, long long m) {
            x %= m;
            if (x < 0) x += m;
            return x;
        }
        
        struct barrett {
            unsigned int _m;
            unsigned long long im;
            
            barrett(unsigned int m) : _m(m), im((unsigned long long)(-1) / m + 1) {}
            
            unsigned int umod() const { return _m; }
            
            unsigned int mul(unsigned int a, unsigned int b) const {
                
                unsigned long long z = a;
                z *= b;
#ifdef _MSC_VER
                unsigned long long x;
                _umul128(z, im, &x);
#else
                unsigned long long x =
                (unsigned long long)(((unsigned __int128)(z)*im) >> 64);
#endif
                unsigned int v = (unsigned int)(z - x * _m);
                if (_m <= v) v += _m;
                return v;
            }
        };
        
        constexpr long long pow_mod_constexpr(long long x, long long n, int m) {
            if (m == 1) return 0;
            unsigned int _m = (unsigned int)(m);
            unsigned long long r = 1;
            unsigned long long y = safe_mod(x, m);
            while (n) {
                if (n & 1) r = (r * y) % _m;
                y = (y * y) % _m;
                n >>= 1;
            }
            return r;
        }
        
        constexpr bool is_prime_constexpr(int n) {
            if (n <= 1) return false;
            if (n == 2 || n == 7 || n == 61) return true;
            if (n % 2 == 0) return false;
            long long d = n - 1;
            while (d % 2 == 0) d /= 2;
            for (long long a : {2, 7, 61}) {
                long long t = d;
                long long y = pow_mod_constexpr(a, t, n);
                while (t != n - 1 && y != 1 && y != n - 1) {
                    y = y * y % n;
                    t <<= 1;
                }
                if (y != n - 1 && t % 2 == 0) {
                    return false;
                }
            }
            return true;
        }
        template <int n> constexpr bool is_prime = is_prime_constexpr(n);
        
        constexpr std::pair<long long, long long> inv_gcd(long long a, long long b) {
            a = safe_mod(a, b);
            if (a == 0) return {b, 0};
            
            long long s = b, t = a;
            long long m0 = 0, m1 = 1;
            
            while (t) {
                long long u = s / t;
                s -= t * u;
                m0 -= m1 * u;  // |m1 * u| <= |m1| * s <= b
                
                
                auto tmp = s;
                s = t;
                t = tmp;
                tmp = m0;
                m0 = m1;
                m1 = tmp;
            }
            if (m0 < 0) m0 += b / s;
            return {s, m0};
        }
        
        constexpr int primitive_root_constexpr(int m) {
            if (m == 2) return 1;
            if (m == 167772161) return 3;
            if (m == 469762049) return 3;
            if (m == 754974721) return 11;
            if (m == 998244353) return 3;
            int divs[20] = {};
            divs[0] = 2;
            int cnt = 1;
            int x = (m - 1) / 2;
            while (x % 2 == 0) x /= 2;
            for (int i = 3; (long long)(i)*i <= x; i += 2) {
                if (x % i == 0) {
                    divs[cnt++] = i;
                    while (x % i == 0) {
                        x /= i;
                    }
                }
            }
            if (x > 1) {
                divs[cnt++] = x;
            }
            for (int g = 2;; g++) {
                bool ok = true;
                for (int i = 0; i < cnt; i++) {
                    if (pow_mod_constexpr(g, (m - 1) / divs[i], m) == 1) {
                        ok = false;
                        break;
                    }
                }
                if (ok) return g;
            }
        }
        template <int m> constexpr int primitive_root = primitive_root_constexpr(m);
        
    }  // namespace internal
    
}  // namespace atcoder

#include <cassert>
#include <numeric>
#include <type_traits>

#ifdef _MSC_VER
#include <intrin.h>
#endif

namespace atcoder {
    
    namespace internal {
        
        struct modint_base {};
        struct static_modint_base : modint_base {};
        
        template <class T> using is_modint = std::is_base_of<modint_base, T>;
        template <class T> using is_modint_t = std::enable_if_t<is_modint<T>::value>;
        
    }  // namespace internal
    
    template <int m, std::enable_if_t<(1 <= m)>* = nullptr>
    struct static_modint : internal::static_modint_base {
        using mint = static_modint;
        
    public:
        static constexpr int mod() { return m; }
        static mint raw(int v) {
            mint x;
            x._v = v;
            return x;
        }
        
        static_modint() : _v(0) {}
        template <class T, internal::is_signed_int_t<T>* = nullptr>
        static_modint(T v) {
            long long x = (long long)(v % (long long)(umod()));
            if (x < 0) x += umod();
            _v = (unsigned int)(x);
        }
        template <class T, internal::is_unsigned_int_t<T>* = nullptr>
        static_modint(T v) {
            _v = (unsigned int)(v % umod());
        }
        static_modint(bool v) { _v = ((unsigned int)(v) % umod()); }
        
        unsigned int val() const { return _v; }
        
        mint& operator++() {
            _v++;
            if (_v == umod()) _v = 0;
            return *this;
        }
        mint& operator--() {
            if (_v == 0) _v = umod();
            _v--;
            return *this;
        }
        mint operator++(int) {
            mint result = *this;
            ++*this;
            return result;
        }
        mint operator--(int) {
            mint result = *this;
            --*this;
            return result;
        }
        
        mint& operator+=(const mint& rhs) {
            _v += rhs._v;
            if (_v >= umod()) _v -= umod();
            return *this;
        }
        mint& operator-=(const mint& rhs) {
            _v -= rhs._v;
            if (_v >= umod()) _v += umod();
            return *this;
        }
        mint& operator*=(const mint& rhs) {
            unsigned long long z = _v;
            z *= rhs._v;
            _v = (unsigned int)(z % umod());
            return *this;
        }
        mint& operator/=(const mint& rhs) { return *this = *this * rhs.inv(); }
        
        mint operator+() const { return *this; }
        mint operator-() const { return mint() - *this; }
        
        mint pow(long long n) const {
            assert(0 <= n);
            mint x = *this, r = 1;
            while (n) {
                if (n & 1) r *= x;
                x *= x;
                n >>= 1;
            }
            return r;
        }
        mint inv() const {
            if (prime) {
                assert(_v);
                return pow(umod() - 2);
            } else {
                auto eg = internal::inv_gcd(_v, m);
                assert(eg.first == 1);
                return eg.second;
            }
        }
        
        friend mint operator+(const mint& lhs, const mint& rhs) {
            return mint(lhs) += rhs;
        }
        friend mint operator-(const mint& lhs, const mint& rhs) {
            return mint(lhs) -= rhs;
        }
        friend mint operator*(const mint& lhs, const mint& rhs) {
            return mint(lhs) *= rhs;
        }
        friend mint operator/(const mint& lhs, const mint& rhs) {
            return mint(lhs) /= rhs;
        }
        friend bool operator==(const mint& lhs, const mint& rhs) {
            return lhs._v == rhs._v;
        }
        friend bool operator!=(const mint& lhs, const mint& rhs) {
            return lhs._v != rhs._v;
        }
        
    private:
        unsigned int _v;
        static constexpr unsigned int umod() { return m; }
        static constexpr bool prime = internal::is_prime<m>;
    };
    
    template <int id> struct dynamic_modint : internal::modint_base {
        using mint = dynamic_modint;
        
    public:
        static int mod() { return (int)(bt.umod()); }
        static void set_mod(int m) {
            assert(1 <= m);
            bt = internal::barrett(m);
        }
        static mint raw(int v) {
            mint x;
            x._v = v;
            return x;
        }
        
        dynamic_modint() : _v(0) {}
        template <class T, internal::is_signed_int_t<T>* = nullptr>
        dynamic_modint(T v) {
            long long x = (long long)(v % (long long)(mod()));
            if (x < 0) x += mod();
            _v = (unsigned int)(x);
        }
        template <class T, internal::is_unsigned_int_t<T>* = nullptr>
        dynamic_modint(T v) {
            _v = (unsigned int)(v % mod());
        }
        dynamic_modint(bool v) { _v = ((unsigned int)(v) % mod()); }
        
        unsigned int val() const { return _v; }
        
        mint& operator++() {
            _v++;
            if (_v == umod()) _v = 0;
            return *this;
        }
        mint& operator--() {
            if (_v == 0) _v = umod();
            _v--;
            return *this;
        }
        mint operator++(int) {
            mint result = *this;
            ++*this;
            return result;
        }
        mint operator--(int) {
            mint result = *this;
            --*this;
            return result;
        }
        
        mint& operator+=(const mint& rhs) {
            _v += rhs._v;
            if (_v >= umod()) _v -= umod();
            return *this;
        }
        mint& operator-=(const mint& rhs) {
            _v += mod() - rhs._v;
            if (_v >= umod()) _v -= umod();
            return *this;
        }
        mint& operator*=(const mint& rhs) {
            _v = bt.mul(_v, rhs._v);
            return *this;
        }
        mint& operator/=(const mint& rhs) { return *this = *this * rhs.inv(); }
        
        mint operator+() const { return *this; }
        mint operator-() const { return mint() - *this; }
        
        mint pow(long long n) const {
            assert(0 <= n);
            mint x = *this, r = 1;
            while (n) {
                if (n & 1) r *= x;
                x *= x;
                n >>= 1;
            }
            return r;
        }
        mint inv() const {
            auto eg = internal::inv_gcd(_v, mod());
            assert(eg.first == 1);
            return eg.second;
        }
        
        friend mint operator+(const mint& lhs, const mint& rhs) {
            return mint(lhs) += rhs;
        }
        friend mint operator-(const mint& lhs, const mint& rhs) {
            return mint(lhs) -= rhs;
        }
        friend mint operator*(const mint& lhs, const mint& rhs) {
            return mint(lhs) *= rhs;
        }
        friend mint operator/(const mint& lhs, const mint& rhs) {
            return mint(lhs) /= rhs;
        }
        friend bool operator==(const mint& lhs, const mint& rhs) {
            return lhs._v == rhs._v;
        }
        friend bool operator!=(const mint& lhs, const mint& rhs) {
            return lhs._v != rhs._v;
        }
        
    private:
        unsigned int _v;
        static internal::barrett bt;
        static unsigned int umod() { return bt.umod(); }
    };
    template <int id> internal::barrett dynamic_modint<id>::bt = 998244353;
    
    using modint998244353 = static_modint<998244353>;
    using modint1000000007 = static_modint<1000000007>;
    using modint = dynamic_modint<-1>;
    
    namespace internal {
        
        template <class T>
        using is_static_modint = std::is_base_of<internal::static_modint_base, T>;
        
        template <class T>
        using is_static_modint_t = std::enable_if_t<is_static_modint<T>::value>;
        
        template <class> struct is_dynamic_modint : public std::false_type {};
        template <int id>
        struct is_dynamic_modint<dynamic_modint<id>> : public std::true_type {};
        
        template <class T>
        using is_dynamic_modint_t = std::enable_if_t<is_dynamic_modint<T>::value>;
        
    }  // namespace internal
    
}  // namespace atcoder

using mint=atcoder::modint;

//Union Find

struct UF{
    int n;
    vector<int> par,size;
    
    void init(int n_){
        n=n_;
        par.assign(n,-1);
        size.assign(n,1);
        
        for(int i=0;i<n;i++){
            par[i]=i;
        }
    }
    
    int root(int a){
        if(par[a]==a) return a;
        else return par[a]=root(par[a]);
    }
    
    void unite(int a,int b){
        if(root(a)!=root(b)){
            size[root(a)]+=size[root(b)];
            par[root(b)]=root(a);
        }
    }
    
    bool check(int a,int b){
        return root(a)==root(b);
    }
};

mint rui[MAX];

int main(){
    
    std::ifstream in("text.txt");
    std::cin.rdbuf(in.rdbuf());
    cin.tie(0);
    ios::sync_with_stdio(false);
    
    mint::set_mod(1000000009);
    
    rui[0]=1;
    for(int i=3;i<MAX;i+=3){
        rui[i]=rui[i-3]*(i-1)*(i-2)/2;
    }
    
    int N,M;cin>>N>>M;
    vector<pair<int,int>> must,ng;
    vector<int> use;
    for(int i=0;i<M;i++){
        int a,b,t;cin>>a>>b>>t;
        a--;b--;
        if(t==0){
            must.push_back(mp(a,b));
        }else{
            ng.push_back(mp(a,b));
        }
        use.push_back(a);
        use.push_back(b);
    }
    
    sort(all(use));
    use.erase(unique(all(use)),use.end());
    
    for(int i=0;i<si(must);i++){
        int a=must[i].fi,b=must[i].se;
        a=lower_bound(all(use),a)-use.begin();
        b=lower_bound(all(use),b)-use.begin();
        must[i]=mp(a,b);
    }
    
    for(int i=0;i<si(ng);i++){
        int a=ng[i].fi,b=ng[i].se;
        a=lower_bound(all(use),a)-use.begin();
        b=lower_bound(all(use),b)-use.begin();
        ng[i]=mp(a,b);
    }
    
    mint ans=0;
    
    for(int bit=0;bit<(1<<si(ng));bit++){
        UF uf;uf.init(si(use));
        for(int i=0;i<si(must);i++){
            int a=must[i].fi,b=must[i].se;
            uf.unite(a,b);
        }
        for(int i=0;i<si(ng);i++){
            int a=ng[i].fi,b=ng[i].se;
            if(bit&(1<<i)) uf.unite(a,b);
        }
        
        bool ok=true;
        int cnt3=0,cnt2=0;
        for(int i=0;i<si(use);i++){
            if(uf.root(i)!=i) continue;
            if(uf.size[i]>3) ok=false;
            if(uf.size[i]==3) cnt3++;
            if(uf.size[i]==2) cnt2++;
        }
        
        if(!ok) continue;
        
        if(cnt2>N) continue;
        
        int rem=3*N-cnt3*3-cnt2*2;
        mint pat=1;
        for(int i=0;i<cnt2;i++){
            pat*=rem;
            rem--;
        }
        pat*=rui[rem];
        
        if((__builtin_popcount(bit))&1) ans-=pat;
        else ans+=pat;
        
    }
    
    cout<<ans.val()<<endl;
}
accuracy: 1
time_ms: 100
memory_kb: 15224
final_rank: 2

code_snippet: #include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<class T>bool chmax(T &a, const T &b) { if (a<b) { a=b; return true; } return false; }
template<class T>bool chmin(T &a, const T &b) { if (b<a) { a=b; return true; } return false; }
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
#define mp make_pair
#define si(x) int(x.size())
const int mod=1000000007,MAX=3000005,INF=1<<30;

//modintのみ

// from: https://gist.github.com/yosupo06/ddd51afb727600fd95d9d8ad6c3c80c9
// (based on AtCoder STL)

#include <cassert>
#include <numeric>
#include <type_traits>

namespace atcoder {
    
    namespace internal {
        
#ifndef _MSC_VER
        template <class T>
        using is_signed_int128 =
        typename std::conditional<std::is_same<T, __int128_t>::value ||
        std::is_same<T, __int128>::value,
        std::true_type,
        std::false_type>::type;
        
        template <class T>
        using is_unsigned_int128 =
        typename std::conditional<std::is_same<T, __uint128_t>::value ||
        std::is_same<T, unsigned __int128>::value,
        std::true_type,
        std::false_type>::type;
        
        template <class T>
        using make_unsigned_int128 =
        typename std::conditional<std::is_same<T, __int128_t>::value,
        __uint128_t,
        unsigned __int128>;
        
        template <class T>
        using is_integral = typename std::conditional<std::is_integral<T>::value ||
        is_signed_int128<T>::value ||
        is_unsigned_int128<T>::value,
        std::true_type,
        std::false_type>::type;
        
        template <class T>
        using is_signed_int = typename std::conditional<(is_integral<T>::value &&
                                                         std::is_signed<T>::value) ||
        is_signed_int128<T>::value,
        std::true_type,
        std::false_type>::type;
        
        template <class T>
        using is_unsigned_int =
        typename std::conditional<(is_integral<T>::value &&
                                   std::is_unsigned<T>::value) ||
        is_unsigned_int128<T>::value,
        std::true_type,
        std::false_type>::type;
        
        template <class T>
        using to_unsigned = typename std::conditional<
        is_signed_int128<T>::value,
        make_unsigned_int128<T>,
        typename std::conditional<std::is_signed<T>::value,
        std::make_unsigned<T>,
        std::common_type<T>>::type>::type;
        
#else
        
        template <class T> using is_integral = typename std::is_integral<T>;
        
        template <class T>
        using is_signed_int =
        typename std::conditional<is_integral<T>::value && std::is_signed<T>::value,
        std::true_type,
        std::false_type>::type;
        
        template <class T>
        using is_unsigned_int =
        typename std::conditional<is_integral<T>::value &&
        std::is_unsigned<T>::value,
        std::true_type,
        std::false_type>::type;
        
        template <class T>
        using to_unsigned = typename std::conditional<is_signed_int<T>::value,
        std::make_unsigned<T>,
        std::common_type<T>>::type;
        
#endif
        
        template <class T>
        using is_signed_int_t = std::enable_if_t<is_signed_int<T>::value>;
        
        template <class T>
        using is_unsigned_int_t = std::enable_if_t<is_unsigned_int<T>::value>;
        
        template <class T> using to_unsigned_t = typename to_unsigned<T>::type;
        
    }  // namespace internal
    
}  // namespace atcoder

#include <utility>

namespace atcoder {
    
    namespace internal {
        
        constexpr long long safe_mod(long long x, long long m) {
            x %= m;
            if (x < 0) x += m;
            return x;
        }
        
        struct barrett {
            unsigned int _m;
            unsigned long long im;
            
            barrett(unsigned int m) : _m(m), im((unsigned long long)(-1) / m + 1) {}
            
            unsigned int umod() const { return _m; }
            
            unsigned int mul(unsigned int a, unsigned int b) const {
                
                unsigned long long z = a;
                z *= b;
#ifdef _MSC_VER
                unsigned long long x;
                _umul128(z, im, &x);
#else
                unsigned long long x =
                (unsigned long long)(((unsigned __int128)(z)*im) >> 64);
#endif
                unsigned int v = (unsigned int)(z - x * _m);
                if (_m <= v) v += _m;
                return v;
            }
        };
        
        constexpr long long pow_mod_constexpr(long long x, long long n, int m) {
            if (m == 1) return 0;
            unsigned int _m = (unsigned int)(m);
            unsigned long long r = 1;
            unsigned long long y = safe_mod(x, m);
            while (n) {
                if (n & 1) r = (r * y) % _m;
                y = (y * y) % _m;
                n >>= 1;
            }
            return r;
        }
        
        constexpr bool is_prime_constexpr(int n) {
            if (n <= 1) return false;
            if (n == 2 || n == 7 || n == 61) return true;
            if (n % 2 == 0) return false;
            long long d = n - 1;
            while (d % 2 == 0) d /= 2;
            for (long long a : {2, 7, 61}) {
                long long t = d;
                long long y = pow_mod_constexpr(a, t, n);
                while (t != n - 1 && y != 1 && y != n - 1) {
                    y = y * y % n;
                    t <<= 1;
                }
                if (y != n - 1 && t % 2 == 0) {
                    return false;
                }
            }
            return true;
        }
        template <int n> constexpr bool is_prime = is_prime_constexpr(n);
        
        constexpr std::pair<long long, long long> inv_gcd(long long a, long long b) {
            a = safe_mod(a, b);
            if (a == 0) return {b, 0};
            
            long long s = b, t = a;
            long long m0 = 0, m1 = 1;
            
            while (t) {
                long long u = s / t;
                s -= t * u;
                m0 -= m1 * u;  // |m1 * u| <= |m1| * s <= b
                
                
                auto tmp = s;
                s = t;
                t = tmp;
                tmp = m0;
                m0 = m1;
                m1 = tmp;
            }
            if (m0 < 0) m0 += b / s;
            return {s, m0};
        }
        
        constexpr int primitive_root_constexpr(int m) {
            if (m == 2) return 1;
            if (m == 167772161) return 3;
            if (m == 469762049) return 3;
            if (m == 754974721) return 11;
            if (m == 998244353) return 3;
            int divs[20] = {};
            divs[0] = 2;
            int cnt = 1;
            int x = (m - 1) / 2;
            while (x % 2 == 0) x /= 2;
            for (int i = 3; (long long)(i)*i <= x; i += 2) {
                if (x % i == 0) {
                    divs[cnt++] = i;
                    while (x % i == 0) {
                        x /= i;
                    }
                }
            }
            if (x > 1) {
                divs[cnt++] = x;
            }
            for (int g = 2;; g++) {
                bool ok = true;
                for (int i = 0; i < cnt; i++) {
                    if (pow_mod_constexpr(g, (m - 1) / divs[i], m) == 1) {
                        ok = false;
                        break;
                    }
                }
                if (ok) return g;
            }
        }
        template <int m> constexpr int primitive_root = primitive_root_constexpr(m);
        
    }  // namespace internal
    
}  // namespace atcoder

#include <cassert>
#include <numeric>
#include <type_traits>

#ifdef _MSC_VER
#include <intrin.h>
#endif

namespace atcoder {
    
    namespace internal {
        
        struct modint_base {};
        struct static_modint_base : modint_base {};
        
        template <class T> using is_modint = std::is_base_of<modint_base, T>;
        template <class T> using is_modint_t = std::enable_if_t<is_modint<T>::value>;
        
    }  // namespace internal
    
    template <int m, std::enable_if_t<(1 <= m)>* = nullptr>
    struct static_modint : internal::static_modint_base {
        using mint = static_modint;
        
    public:
        static constexpr int mod() { return m; }
        static mint raw(int v) {
            mint x;
            x._v = v;
            return x;
        }
        
        static_modint() : _v(0) {}
        template <class T, internal::is_signed_int_t<T>* = nullptr>
        static_modint(T v) {
            long long x = (long long)(v % (long long)(umod()));
            if (x < 0) x += umod();
            _v = (unsigned int)(x);
        }
        template <class T, internal::is_unsigned_int_t<T>* = nullptr>
        static_modint(T v) {
            _v = (unsigned int)(v % umod());
        }
        static_modint(bool v) { _v = ((unsigned int)(v) % umod()); }
        
        unsigned int val() const { return _v; }
        
        mint& operator++() {
            _v++;
            if (_v == umod()) _v = 0;
            return *this;
        }
        mint& operator--() {
            if (_v == 0) _v = umod();
            _v--;
            return *this;
        }
        mint operator++(int) {
            mint result = *this;
            ++*this;
            return result;
        }
        mint operator--(int) {
            mint result = *this;
            --*this;
            return result;
        }
        
        mint& operator+=(const mint& rhs) {
            _v += rhs._v;
            if (_v >= umod()) _v -= umod();
            return *this;
        }
        mint& operator-=(const mint& rhs) {
            _v -= rhs._v;
            if (_v >= umod()) _v += umod();
            return *this;
        }
        mint& operator*=(const mint& rhs) {
            unsigned long long z = _v;
            z *= rhs._v;
            _v = (unsigned int)(z % umod());
            return *this;
        }
        mint& operator/=(const mint& rhs) { return *this = *this * rhs.inv(); }
        
        mint operator+() const { return *this; }
        mint operator-() const { return mint() - *this; }
        
        mint pow(long long n) const {
            assert(0 <= n);
            mint x = *this, r = 1;
            while (n) {
                if (n & 1) r *= x;
                x *= x;
                n >>= 1;
            }
            return r;
        }
        mint inv() const {
            if (prime) {
                assert(_v);
                return pow(umod() - 2);
            } else {
                auto eg = internal::inv_gcd(_v, m);
                assert(eg.first == 1);
                return eg.second;
            }
        }
        
        friend mint operator+(const mint& lhs, const mint& rhs) {
            return mint(lhs) += rhs;
        }
        friend mint operator-(const mint& lhs, const mint& rhs) {
            return mint(lhs) -= rhs;
        }
        friend mint operator*(const mint& lhs, const mint& rhs) {
            return mint(lhs) *= rhs;
        }
        friend mint operator/(const mint& lhs, const mint& rhs) {
            return mint(lhs) /= rhs;
        }
        friend bool operator==(const mint& lhs, const mint& rhs) {
            return lhs._v == rhs._v;
        }
        friend bool operator!=(const mint& lhs, const mint& rhs) {
            return lhs._v != rhs._v;
        }
        
    private:
        unsigned int _v;
        static constexpr unsigned int umod() { return m; }
        static constexpr bool prime = internal::is_prime<m>;
    };
    
    template <int id> struct dynamic_modint : internal::modint_base {
        using mint = dynamic_modint;
        
    public:
        static int mod() { return (int)(bt.umod()); }
        static void set_mod(int m) {
            assert(1 <= m);
            bt = internal::barrett(m);
        }
        static mint raw(int v) {
            mint x;
            x._v = v;
            return x;
        }
        
        dynamic_modint() : _v(0) {}
        template <class T, internal::is_signed_int_t<T>* = nullptr>
        dynamic_modint(T v) {
            long long x = (long long)(v % (long long)(mod()));
            if (x < 0) x += mod();
            _v = (unsigned int)(x);
        }
        template <class T, internal::is_unsigned_int_t<T>* = nullptr>
        dynamic_modint(T v) {
            _v = (unsigned int)(v % mod());
        }
        dynamic_modint(bool v) { _v = ((unsigned int)(v) % mod()); }
        
        unsigned int val() const { return _v; }
        
        mint& operator++() {
            _v++;
            if (_v == umod()) _v = 0;
            return *this;
        }
        mint& operator--() {
            if (_v == 0) _v = umod();
            _v--;
            return *this;
        }
        mint operator++(int) {
            mint result = *this;
            ++*this;
            return result;
        }
        mint operator--(int) {
            mint result = *this;
            --*this;
            return result;
        }
        
        mint& operator+=(const mint& rhs) {
            _v += rhs._v;
            if (_v >= umod()) _v -= umod();
            return *this;
        }
        mint& operator-=(const mint& rhs) {
            _v += mod() - rhs._v;
            if (_v >= umod()) _v -= umod();
            return *this;
        }
        mint& operator*=(const mint& rhs) {
            _v = bt.mul(_v, rhs._v);
            return *this;
        }
        mint& operator/=(const mint& rhs) { return *this = *this * rhs.inv(); }
        
        mint operator+() const { return *this; }
        mint operator-() const { return mint() - *this; }
        
        mint pow(long long n) const {
            assert(0 <= n);
            mint x = *this, r = 1;
            while (n) {
                if (n & 1) r *= x;
                x *= x;
                n >>= 1;
            }
            return r;
        }
        mint inv() const {
            auto eg = internal::inv_gcd(_v, mod());
            assert(eg.first == 1);
            return eg.second;
        }
        
        friend mint operator+(const mint& lhs, const mint& rhs) {
            return mint(lhs) += rhs;
        }
        friend mint operator-(const mint& lhs, const mint& rhs) {
            return mint(lhs) -= rhs;
        }
        friend mint operator*(const mint& lhs, const mint& rhs) {
            return mint(lhs) *= rhs;
        }
        friend mint operator/(const mint& lhs, const mint& rhs) {
            return mint(lhs) /= rhs;
        }
        friend bool operator==(const mint& lhs, const mint& rhs) {
            return lhs._v == rhs._v;
        }
        friend bool operator!=(const mint& lhs, const mint& rhs) {
            return lhs._v != rhs._v;
        }
        
    private:
        unsigned int _v;
        static internal::barrett bt;
        static unsigned int umod() { return bt.umod(); }
    };
    template <int id> internal::barrett dynamic_modint<id>::bt = 998244353;
    
    using modint998244353 = static_modint<998244353>;
    using modint1000000007 = static_modint<1000000007>;
    using modint = dynamic_modint<-1>;
    
    namespace internal {
        
        template <class T>
        using is_static_modint = std::is_base_of<internal::static_modint_base, T>;
        
        template <class T>
        using is_static_modint_t = std::enable_if_t<is_static_modint<T>::value>;
        
        template <class> struct is_dynamic_modint : public std::false_type {};
        template <int id>
        struct is_dynamic_modint<dynamic_modint<id>> : public std::true_type {};
        
        template <class T>
        using is_dynamic_modint_t = std::enable_if_t<is_dynamic_modint<T>::value>;
        
    }  // namespace internal
    
}  // namespace atcoder

using mint=atcoder::modint;

//Union Find

struct UF{
    int n;
    vector<int> par,size;
    
    void init(int n_){
        n=n_;
        par.assign(n,-1);
        size.assign(n,1);
        
        for(int i=0;i<n;i++){
            par[i]=i;
        }
    }
    
    int root(int a){
        if(par[a]==a) return a;
        else return par[a]=root(par[a]);
    }
    
    void unite(int a,int b){
        if(root(a)!=root(b)){
            size[root(a)]+=size[root(b)];
            par[root(b)]=root(a);
        }
    }
    
    bool check(int a,int b){
        return root(a)==root(b);
    }
};

mint rui[MAX];

int main(){
    
    std::ifstream in("text.txt");
    std::cin.rdbuf(in.rdbuf());
    cin.tie(0);
    ios::sync_with_stdio(false);
    
    rui[0]=1;
    for(int i=3;i<MAX;i+=3){
        rui[i]=rui[i-3]*(i-1)*(i-2)/2;
    }
    
    mint::set_mod(1000000009);
    
    int N,M;cin>>N>>M;
    vector<pair<int,int>> must,ng;
    vector<int> use;
    for(int i=0;i<M;i++){
        int a,b,t;cin>>a>>b>>t;
        a--;b--;
        if(t==0){
            must.push_back(mp(a,b));
        }else{
            ng.push_back(mp(a,b));
        }
        use.push_back(a);
        use.push_back(b);
    }
    
    sort(all(use));
    use.erase(unique(all(use)),use.end());
    
    for(int i=0;i<si(must);i++){
        int a=must[i].fi,b=must[i].se;
        a=lower_bound(all(use),a)-use.begin();
        b=lower_bound(all(use),b)-use.begin();
        must[i]=mp(a,b);
    }
    
    for(int i=0;i<si(ng);i++){
        int a=ng[i].fi,b=ng[i].se;
        a=lower_bound(all(use),a)-use.begin();
        b=lower_bound(all(use),b)-use.begin();
        ng[i]=mp(a,b);
    }
    
    mint ans=0;
    
    for(int bit=0;bit<(1<<si(ng));bit++){
        UF uf;uf.init(si(use));
        for(int i=0;i<si(must);i++){
            int a=must[i].fi,b=must[i].se;
            uf.unite(a,b);
        }
        for(int i=0;i<si(ng);i++){
            int a=ng[i].fi,b=ng[i].se;
            if(bit&(1<<i)) uf.unite(a,b);
        }
        
        bool ok=true;
        int cnt3=0,cnt2=0;
        for(int i=0;i<si(use);i++){
            if(uf.root(i)!=i) continue;
            if(uf.size[i]>3) ok=false;
            if(uf.size[i]==3) cnt3++;
            if(uf.size[i]==2) cnt2++;
        }
        
        if(!ok) continue;
        
        if(cnt2>N) continue;
        
        int rem=3*N-cnt3*3-cnt2*2;
        mint pat=1;
        for(int i=0;i<cnt2;i++){
            pat*=rem;
            rem--;
        }
        pat*=rui[rem];
        
        if((__builtin_popcount(bit))&1) ans-=pat;
        else ans+=pat;
        
    }
    
    cout<<ans.val()<<endl;
}
accuracy: 0.2708333333333333
time_ms: 40
memory_kb: 15180
final_rank: 18

code_snippet: #pragma GCC optimize("O3")
#pragma GCC optimize("unroll-loops")
#include<iostream>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<algorithm>
#include<functional>
#include<iomanip>
#include<queue>
#include<ciso646>
#include<random>
#include<map>
#include<set>
#include<bitset>
#include<stack>
#include<unordered_map>
#include<unordered_set>
#include<utility>
#include<cassert>
#include<complex>
#include<numeric>
#include<array>
using namespace std;

//#define int long long
typedef long long ll;

typedef unsigned long long ul;
typedef unsigned int ui;
constexpr ll mod = 1000000009;
const ll INF = mod * mod;
typedef pair<int, int>P;
#define stop char nyaa;cin>>nyaa;
#define rep(i,n) for(int i=0;i<n;i++)
#define per(i,n) for(int i=n-1;i>=0;i--)
#define Rep(i,sta,n) for(int i=sta;i<n;i++)
#define rep1(i,n) for(int i=1;i<=n;i++)
#define per1(i,n) for(int i=n;i>=1;i--)
#define Rep1(i,sta,n) for(int i=sta;i<=n;i++)
#define all(v) (v).begin(),(v).end()
typedef pair<ll, ll> LP;
typedef double ld;
typedef pair<ld, ld> LDP;
const ld eps = 1e-8;
const ld pi = acosl(-1.0);

ll mod_pow(ll x, ll n, ll m = mod) {
	if (n < 0) {
		ll res = mod_pow(x, -n, m);
		return mod_pow(res, m - 2, m);
	}
	if (abs(x) >= m)x %= m;
	if (x < 0)x += m;
	ll res = 1;
	while (n) {
		if (n & 1)res = res * x % m;
		x = x * x % m; n >>= 1;
	}
	return res;
}
struct modint {
	ll n;
	modint() :n(0) { ; }
	modint(ll m) :n(m) {
		if (n >= mod)n %= mod;
		else if (n < 0)n = (n % mod + mod) % mod;
	}
	operator int() { return n; }
};
bool operator==(modint a, modint b) { return a.n == b.n; }
modint operator+=(modint& a, modint b) { a.n += b.n; if (a.n >= mod)a.n -= mod; return a; }
modint operator-=(modint& a, modint b) { a.n -= b.n; if (a.n < 0)a.n += mod; return a; }
modint operator*=(modint& a, modint b) { a.n = ((ll)a.n * b.n) % mod; return a; }
modint operator+(modint a, modint b) { return a += b; }
modint operator-(modint a, modint b) { return a -= b; }
modint operator*(modint a, modint b) { return a *= b; }
modint operator^(modint a, ll n) {
	if (n == 0)return modint(1);
	modint res = (a * a) ^ (n / 2);
	if (n % 2)res = res * a;
	return res;
}

ll inv(ll a, ll p) {
	return (a == 1 ? 1 : (1 - p * inv(p % a, a)) / a + p);
}
modint operator/(modint a, modint b) { return a * modint(inv(b, mod)); }
modint operator/=(modint& a, modint b) { a = a / b; return a; }
const int max_n = 1 << 1;
modint fact[max_n], factinv[max_n];
void init_f() {
	fact[0] = modint(1);
	for (int i = 0; i < max_n - 1; i++) {
		fact[i + 1] = fact[i] * modint(i + 1);
	}
	factinv[max_n - 1] = modint(1) / fact[max_n - 1];
	for (int i = max_n - 2; i >= 0; i--) {
		factinv[i] = factinv[i + 1] * modint(i + 1);
	}
}
modint comb(int a, int b) {
	if (a < 0 || b < 0 || a < b)return 0;
	return fact[a] * factinv[b] * factinv[a - b];
}
modint combP(int a, int b) {
	if (a < 0 || b < 0 || a < b)return 0;
	return fact[a] * factinv[a - b];
}

void solve() {
	int n, m; cin >> n >> m;
	vector<int> a(m), b(m), c(m);
	rep(i, m) {
		cin >> a[i] >> b[i] >> c[i]; a[i]--; b[i]--;
	}
	vector<int> vs;
	rep(i, m) {
		vs.push_back(a[i]);
		vs.push_back(b[i]);
	}
	sort(all(vs));
	vs.erase(unique(all(vs)), vs.end());
	rep(i, m) {
		a[i] = lower_bound(all(vs), a[i]) - vs.begin();
		b[i] = lower_bound(all(vs), b[i]) - vs.begin();
	}
	vector<int> ones;
	rep(i, m)if (c[i] == 1)ones.push_back(i);
	vector<modint> dp(3 * n + 1);
	dp[0] = 1;
	modint inv2 = (1 + mod) / 2;
	for (int i = 3; i <= 3 * n; i+=3) {
		dp[i] = dp[i - 3] * (modint)(i - 1) * (modint)(i - 2) * inv2;
	}
	modint ans = 0;
	int len = ones.size();
	rep(i, (1 << len)) {
		vector<vector<int>> G(vs.size());
		rep(j, m)if (c[j] == 0) {
			G[a[j]].push_back(b[j]);
			G[b[j]].push_back(a[j]);
		}
		rep(j, len)if (i & (1 << j)) {
			int id = ones[j];
			G[a[id]].push_back(b[id]);
			G[b[id]].push_back(a[id]);
		}
		int c1 = 3*n - vs.size();
		int c2 = 0;
		int c4 = 0;
		vector<bool> used(vs.size());
		rep(j, vs.size()) {
			if (used[j])continue;
			int cnt = 1; used[j] = true;
			queue<int> q; q.push(j);
			while (!q.empty()) {
				int id = q.front(); q.pop();
				for (int to : G[id]) {
					if (!used[to]) {
						used[to] = true; cnt++;
						q.push(to);
					}
				}
			}
			if (cnt == 1)c1++;
			else if (cnt == 2)c2++;
			else if (cnt != 3)c4++;
		}
		if (c4)continue;
		if (c1 < c2)continue;
		modint num = 1;
		while (c2 > 0) {
			num *= c1;
			c1--; c2--;
		}
		num *= dp[c1];
		int coef = 1;
		rep(j, len)if (i & (1 << j))coef *= -1;
		ans += (modint)coef * num;
	}
	cout << ans << "\n";
}


signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout << fixed << setprecision(10);
	//init_f();
	//init();
	//expr();
	//int t; cin >> t;rep(i, t)
	//while(cin>>n,n)
	solve();
	return 0;
}
accuracy: 1
time_ms: 370
memory_kb: 26408
final_rank: 8