#include using namespace std; using Wuf = int; Wuf ng = -1; class WeightedUnionFind{ public: vector par,siz; vector rootweight; Wuf op(Wuf a,Wuf b){return a^b;} //多分plusかxorだけ. Wuf inv(Wuf a){return a;} //逆元?. WeightedUnionFind(int N){ par.resize(N,-1); siz.resize(N,1); rootweight.resize(N); } int root(int x){ if(par.at(x) == -1) return x; else{ int r = root(par.at(x)); rootweight.at(x) = op(rootweight.at(x),rootweight.at(par.at(x))); return par.at(x) = r; } } Wuf weight(int x){ root(x); //par等を更新する. return rootweight.at(x); } Wuf diffweight(int u, int v){ //return Av-Au if(!issame(u,v)) return ng; return op(weight(v),inv(weight(u))); } bool unite(int u,int v,Wuf w){ //Av-Au=w. w = op(w,weight(u)); w = op(w,inv(weight(v))); u = root(u); v = root(v); if(u == v) return false; if(siz.at(u) < siz.at(v)) swap(u,v),w = inv(w); par.at(v) = u; siz.at(u) += siz.at(v); rootweight.at(v) = w; return true; } bool unite2(int u,int v,Wuf w){ //本来の辺と矛盾するならfalseを返す. if(root(u) == root(v)) return diffweight(u,v) == w; w = op(w,weight(u)); w = op(w,inv(weight(v))); u = root(u); v = root(v); if(siz.at(u) < siz.at(v)) swap(u,v),w = inv(w); par.at(v) = u; siz.at(u) += siz.at(v); rootweight.at(v) = w; return true; } bool issame(int u, int v){ if(root(u) == root(v)) return true; else return false; } }; int main(){ ios_base::sync_with_stdio(false); cin.tie(nullptr); int N,M; cin >> N >> M; int mul = N; WeightedUnionFind Z(N); while(M--){ int t,a,b; cin >> t >> a >> b,a--,b--; if(Z.issame(a,b)){ if(Z.diffweight(a,b) != t){cout << "0\n"; return 0;} } else Z.unite(a,b,t),mul--; } int answer = 1; while(mul--){ answer += answer; if(answer >= 998244353) answer -= 998244353; } cout << answer << endl; }