#include using namespace std; using ll = long long; static constexpr int MOD = 998244353; static inline ll modpow(ll a, ll e) { ll r = 1 % MOD; a %= MOD; while (e > 0) { if (e & 1) r = (r * a) % MOD; a = (a * a) % MOD; e >>= 1; } return r; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int K; cin >> K; vector L(K), M(K); ll N = 0; for (int i = 0; i < K; i++) { cin >> L[i] >> M[i]; N += (ll)L[i] * (ll)M[i]; } // 制約で N <= 1e8 int Nint = (int)N; // 必要な階乗の引数だけ集める vector need; need.reserve(2 * K + 2); need.push_back(0); need.push_back(Nint); for (int i = 0; i < K; i++) { need.push_back(L[i]); need.push_back(M[i]); } sort(need.begin(), need.end()); need.erase(unique(need.begin(), need.end()), need.end()); // factorial 値を必要な点だけ保持 unordered_map fact; fact.reserve(need.size() * 2); fact[0] = 1; ll cur = 1; int p = 1; // need[0]=0 はセット済み for (int x = 1; x <= Nint; x++) { cur = (cur * x) % MOD; while (p < (int)need.size() && need[p] == x) { fact[x] = (int)cur; p++; } } // factorial の逆元をキャッシュ(必要な分だけ) unordered_map invfact; invfact.reserve(need.size() * 2); auto get_inv_fact = [&](int x) -> ll { auto it = invfact.find(x); if (it != invfact.end()) return it->second; ll inv = modpow(fact[x], MOD - 2); invfact[x] = (int)inv; return inv; }; // 答え = N! / ∏ ( (L_i!)^{M_i} * (M_i)! ) ll ans = fact[Nint]; for (int i = 0; i < K; i++) { ans = ans * get_inv_fact(M[i]) % MOD; // / (M_i)! ll invL = get_inv_fact(L[i]); // (L_i!)^{-1} ans = ans * modpow(invL, (ll)M[i]) % MOD; // / (L_i!)^{M_i} } cout << ans % MOD << '\n'; return 0; }