結果
| 問題 | No.3004 ヤング図形 |
| コンテスト | |
| ユーザー |
|
| 提出日時 | 2026-01-22 11:20:40 |
| 言語 | C++23 (gcc 15.2.0 + boost 1.89.0) |
| 結果 |
AC
|
| 実行時間 | 526 ms / 4,000 ms |
| コード長 | 2,102 bytes |
| 記録 | |
| コンパイル時間 | 3,863 ms |
| コンパイル使用メモリ | 351,084 KB |
| 実行使用メモリ | 7,852 KB |
| 最終ジャッジ日時 | 2026-01-22 11:20:56 |
| 合計ジャッジ時間 | 14,459 ms |
|
ジャッジサーバーID (参考情報) |
judge3 / judge1 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| sample | AC * 3 |
| other | AC * 25 |
ソースコード
#include <bits/stdc++.h>
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<int> 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<int> 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<int, int> 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<int, int> 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;
}