/*HDU 6073 - Matching In Multiplication [ 图论 ] | 2017 Multi-University Training Contest 4题意: 定义一张二分图,U中每个节点和V中两个节点连边 完美匹配的权值为该匹配所有边的权值相乘 求所有完美匹配的权值之和分析: 可以发现有些V中的点只能连唯一的U中的点 按拓扑排序思路将这些全部处理掉后,剩下的点构成一个个环 每个环有两种连线方式,间隔取边权*/#includeusing namespace std;#define LL long longconst int N = 600005;const LL MOD = 998244353;struct Edge { int v; LL w;};vector G[N];int vis[N];int cnt[N];int t, n;LL ans;queue que;LL s[2];void dfs(int x, int pre, int p){ if (vis[x]) return; vis[x] = 2; for (const auto & e : G[x]) { if (vis[e.v] == 1 || e.v == pre) continue; s[p] = s[p] * e.w % MOD; dfs(e.v, x, p^1); break; }}void solve(){ for (int i = n+1; i <= 2*n; i++) if (G[i].size() == 1) que.push(i); ans = 1; while (!que.empty()) { int y = que.front(); que.pop(); vis[y] = 1; for (const auto& e: G[y]) { if (!vis[e.v]) { vis[e.v] = 1; ans = ans * e.w % MOD; for (const auto & ee: G[e.v]) { if (vis[ee.v]) continue; cnt[ee.v]--; if (cnt[ee.v] == 1) que.push(ee.v); } } } } for (int i = 1; i <= n; i++) { if (!vis[i]) { s[0] = s[1] = 1; dfs(i, i, 0); ans = ans * (s[0] + s[1]) % MOD; } }}void init(int n){ for (int i = 0; i <= n; i++) G[i].clear(); while (!que.empty()) que.pop(); memset(vis, 0, sizeof(vis)); memset(cnt, 0, sizeof(cnt));}int main(){ scanf("%d", &t); while (t--) { scanf("%d", &n); init(n<<1); for (int i = 1; i <= n; i++) { int v; LL w; scanf("%d%lld", &v, &w); v += n; G[i].push_back(Edge{v, w}); G[v].push_back(Edge{i, w}); cnt[v]++; scanf("%d%lld", &v, &w); v += n; G[i].push_back(Edge{v, w}); G[v].push_back(Edge{i, w}); cnt[v]++; } solve(); printf("%lld\n", ans); }}