// compile: make data
// run: ./data < data.in
#include <bits/stdc++.h>
using namespace std;
#pragma GCC optimize("O3,unroll-loops")
#pragma GCC target("avx2,bmi,bmi2,lzcnt,popcnt")
#ifdef LOCAL
#include <debug/codeforces.h>
#define debug(x...) _debug_print(#x, x);
#else
#define debug(x...) {};
#endif
template<typename...Args> void print_(Args...args){((cout<<args<<" "),...)<<endl;}
#define rep(i,a,b) for(int i=(a);i<(int)(b);++i)
#define sz(v) ((int)(v).size())
#define print(...) print_(__VA_ARGS__);
#define INTMAX (int)(9223372036854775807)
#define INF (int)(1152921504606846976)
#define double long double
#define int long long
#define MAXN 300010
#define P 998244353
vector<int> C(MAXN, 0);
constexpr long long safe_mod(long long x, long long m) {
x %= m;
if (x < 0) x += m;
return x;
}
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;
auto tmp = s;
s = t;
t = tmp;
tmp = m0;
m0 = m1;
m1 = tmp;
}
if (m0 < 0) m0 += b / s;
return {s, m0};
}
long long inv_mod(long long x, long long m) {
assert(1 <= m);
auto z = inv_gcd(x, m);
assert(z.first == 1);
return z.second;
}
struct seg_tree {
struct node {
int l, r;
node *ch[2];
int val, lazy;
};
int cnt = 0;
node *root;
vector<node> pool;
seg_tree(int l, int r) {
int n = r - l + 1;
pool.resize(2 * n + 10);
root = newnode(l, r);
build(root, l, r);
}
node *newnode(int l, int r, int val = 0) {
pool[cnt] = {l, r, {nullptr, nullptr}, val, 0};
return &pool[cnt++];
}
void pushup(node *x) {
x->val = x->ch[0]->val ^ x->ch[1]->val;
}
void pushdown(node *x) {
x->ch[0]->lazy ^= x->lazy;
x->ch[1]->lazy ^= x->lazy;
x->lazy = 0;
}
void build(node *x, int l, int r) {
if (l == r) {
x->val = 0;
return;
}
int mid = (l+r) >> 1;
x->ch[0] = newnode(l, mid);
x->ch[1] = newnode(mid+1, r);
build(x->ch[0], l, mid);
build(x->ch[1], mid+1, r);
pushup(x);
}
void modify(node *x, int ql, int qr, int v) {
if (ql > x->r || qr < x->l) return;
if (ql <= x->l && x->r <= qr) {
x->lazy ^= v;
return;
}
modify(x->ch[0], ql, qr, v);
modify(x->ch[1], ql, qr, v);
pushup(x);
}
int query(node *x, int id) {
if (id < x->l || id > x->r) return 0;
if (x->l == x->r) return x->lazy ^ x->val;
pushdown(x);
return query(x->ch[0], id) ^ query(x->ch[1], id);
}
};
void preprocess() {
auto mul([&](int a, int b) {
return (a * b) % P;
});
auto div([&](int a, int b) {
return mul(a, inv_mod(b, P));
});
C[0] = 1;
rep(i, 0, sz(C)/2 - 1) {
C[2*(i+1)] = mul(C[2*i], 2*i + 1);
C[2*(i+1)] = mul(C[2*(i+1)], 2);
C[2*(i+1)] = div(C[2*(i+1)], i + 2);
}
}
void solve() {
mt19937_64 gen(chrono::steady_clock::now().time_since_epoch().count());
// mt19937_64 gen(1);
int n, q; cin >> n >> q;
// vector<int> C(n+10, 0);
// preprocess(C);
vector<pair<int, int>> itv(q);
vector<int> r(q);
rep(i, 0, q) {
cin >> itv[i].first >> itv[i].second;
--itv[i].first, --itv[i].second;
r[i] = gen();
}
seg_tree t(0, n-1);
vector<int> x(n, 0);
rep(i, 0, q) t.modify(t.root, itv[i].first, itv[i].second, r[i]);
map<int, int> m;
rep(i, 0, n) ++m[t.query(t.root, i)];
int ans = 1;
for (auto [k, v]: m) ans = (ans * C[v]) % P;
cout << ans << endl;
}
int32_t main() {
ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
preprocess();
int _; cin >> _;
while (_--) solve();
return 0;
}
No Comments