#ifdef LOCAL #include #else #pragma GCC optimize("O3,unroll-loops") #pragma GCC target("avx2,popcnt,lzcnt,abm,bmi,bmi2") #include #define debug(...) ((void)0) #define postprocess(...) ((void)0) #endif #include "atcoder/modint.hpp" using mint = atcoder::modint998244353; using namespace std; using ll = long long; using ld = long double; struct WeightedUnionFind { vector d; vector p; WeightedUnionFind(int n = 0) : d(n, -1), p(n, 0) {} int find(int x) { if (d[x] < 0) return x; int root = find(d[x]); p[x] ^= p[d[x]]; return d[x] = root; } // w = weight(y) ^ weight(x) bool unite(int x, int y, int w) { w ^= potential(x) ^ potential(y); x = find(x); y = find(y); if (x == y) return false; if (d[x] > d[y]) swap(x, y); d[x] += d[y]; d[y] = x; p[y] = w; return true; } bool same(int x, int y) { return find(x) == find(y); } int size(int x) { return -d[find(x)]; } // potential(x) := weight(x) ^ weight(root(x)) int potential(int x) { find(x); return p[x]; } }; void solve([[maybe_unused]] int test) { int N, Q; cin >> N >> Q; WeightedUnionFind uf(N); int A[Q], B[Q], C[Q]; for (int i = 0; i < Q; i++) { cin >> A[i] >> B[i] >> C[i]; A[i]--; B[i]--; uf.unite(A[i], B[i], C[i]); } for (int i = 0; i < Q; i++) { if ((uf.potential(A[i]) ^ uf.potential(B[i])) != C[i]) { cout << 0 << endl; return; } } mint ans = 1; set roots; for (int i = 0; i < N; i++) { roots.insert(uf.find(i)); } for (auto&& x : roots) { ans *= 2; } cout << ans.val() << endl; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t = 1; // cin >> t; for (int i = 1; i <= t; i++) { solve(i); } postprocess(); }