結果
問題 | No.2580 Hyperinflation |
ユーザー | suisen |
提出日時 | 2023-12-08 02:20:05 |
言語 | C++17 (gcc 12.3.0 + boost 1.83.0) |
結果 |
TLE
|
実行時間 | - |
コード長 | 33,958 bytes |
コンパイル時間 | 7,222 ms |
コンパイル使用メモリ | 226,592 KB |
実行使用メモリ | 245,072 KB |
最終ジャッジ日時 | 2024-09-27 02:35:54 |
合計ジャッジ時間 | 19,075 ms |
ジャッジサーバーID (参考情報) |
judge4 / judge3 |
(要ログイン)
テストケース
テストケース表示入力 | 結果 | 実行時間 実行使用メモリ |
---|---|---|
testcase_00 | AC | 196 ms
245,072 KB |
testcase_01 | AC | 178 ms
120,548 KB |
testcase_02 | AC | 178 ms
120,640 KB |
testcase_03 | AC | 178 ms
120,656 KB |
testcase_04 | AC | 182 ms
120,668 KB |
testcase_05 | AC | 177 ms
120,580 KB |
testcase_06 | AC | 178 ms
120,576 KB |
testcase_07 | AC | 181 ms
120,676 KB |
testcase_08 | AC | 177 ms
120,492 KB |
testcase_09 | AC | 176 ms
120,576 KB |
testcase_10 | AC | 177 ms
120,552 KB |
testcase_11 | AC | 181 ms
120,576 KB |
testcase_12 | AC | 183 ms
120,596 KB |
testcase_13 | AC | 180 ms
120,576 KB |
testcase_14 | AC | 187 ms
120,768 KB |
testcase_15 | AC | 188 ms
120,796 KB |
testcase_16 | AC | 187 ms
120,624 KB |
testcase_17 | AC | 179 ms
120,548 KB |
testcase_18 | AC | 680 ms
121,952 KB |
testcase_19 | AC | 683 ms
122,112 KB |
testcase_20 | AC | 690 ms
122,084 KB |
testcase_21 | AC | 680 ms
121,984 KB |
testcase_22 | AC | 691 ms
121,984 KB |
testcase_23 | TLE | - |
testcase_24 | -- | - |
testcase_25 | -- | - |
testcase_26 | -- | - |
testcase_27 | -- | - |
testcase_28 | -- | - |
testcase_29 | -- | - |
testcase_30 | -- | - |
testcase_31 | -- | - |
testcase_32 | -- | - |
testcase_33 | -- | - |
ソースコード
// N^3 #pragma GCC target("avx2") #pragma GCC optimize("O3") #pragma GCC optimize("unroll-loops") #include <bit> #include <iostream> #include <vector> #include <atcoder/modint> #include <atcoder/convolution> using mint = atcoder::modint998244353; #include <cassert> #include <vector> #include <iomanip> #include <iostream> #include <sstream> #include <vector> #include <atcoder/convolution> namespace suisen::internal { template <typename T, typename R = T> std::vector<R> convolution_naive(const std::vector<T>& a, const std::vector<T>& b) { const int n = a.size(), m = b.size(); std::vector<R> c(n + m - 1); if (n < m) { for (int j = 0; j < m; j++) for (int i = 0; i < n; i++) c[i + j] += R(a[i]) * b[j]; } else { for (int i = 0; i < n; i++) for (int j = 0; j < m; j++) c[i + j] += R(a[i]) * b[j]; } return c; } } // namespace suisen namespace suisen { template <typename mint, atcoder::internal::is_modint_t<mint>* = nullptr> std::vector<mint> arbitrary_mod_convolution(const std::vector<mint>& a, const std::vector<mint>& b) { int n = int(a.size()), m = int(b.size()); if constexpr (atcoder::internal::is_static_modint<mint>::value) { int maxz = 1; while (not ((mint::mod() - 1) & maxz)) maxz <<= 1; int z = 1; while (z < n + m - 1) z <<= 1; if (z <= maxz) return atcoder::convolution<mint>(a, b); } if (n == 0 or m == 0) return {}; if (std::min(n, m) <= 120) return internal::convolution_naive(a, b); static constexpr long long MOD1 = 754974721; // 2^24 static constexpr long long MOD2 = 167772161; // 2^25 static constexpr long long MOD3 = 469762049; // 2^26 static constexpr long long M1M2 = MOD1 * MOD2; static constexpr long long INV_M1_MOD2 = atcoder::internal::inv_gcd(MOD1, MOD2).second; static constexpr long long INV_M1M2_MOD3 = atcoder::internal::inv_gcd(M1M2, MOD3).second; std::vector<int> a2(n), b2(m); for (int i = 0; i < n; ++i) a2[i] = a[i].val(); for (int i = 0; i < m; ++i) b2[i] = b[i].val(); auto c1 = atcoder::convolution<MOD1>(a2, b2); auto c2 = atcoder::convolution<MOD2>(a2, b2); auto c3 = atcoder::convolution<MOD3>(a2, b2); const long long m1m2 = mint(M1M2).val(); std::vector<mint> c(n + m - 1); for (int i = 0; i < n + m - 1; ++i) { // Garner's Algorithm // X = x1 + x2 * m1 + x3 * m1 * m2 // x1 = c1[i], x2 = (c2[i] - x1) / m1 (mod m2), x3 = (c3[i] - x1 - x2 * m1) / m2 (mod m3) long long x1 = c1[i]; long long x2 = (atcoder::static_modint<MOD2>(c2[i] - x1) * INV_M1_MOD2).val(); long long x3 = (atcoder::static_modint<MOD3>(c3[i] - x1 - x2 * MOD1) * INV_M1M2_MOD3).val(); c[i] = x1 + x2 * MOD1 + x3 * m1m2; } return c; } std::vector<__uint128_t> convolution_int(const std::vector<int> &a, const std::vector<int> &b) { int n = int(a.size()), m = int(b.size()); auto check_nonnegative = [](int e) { return e >= 0; }; assert(std::all_of(a.begin(), a.end(), check_nonnegative)); assert(std::all_of(b.begin(), b.end(), check_nonnegative)); if (n == 0 or m == 0) return {}; if (std::min(n, m) <= 120) return internal::convolution_naive<int, __uint128_t>(a, b); static constexpr long long MOD1 = 754974721; // 2^24 static constexpr long long MOD2 = 167772161; // 2^25 static constexpr long long MOD3 = 469762049; // 2^26 static constexpr long long M1M2 = MOD1 * MOD2; static constexpr long long INV_M1_MOD2 = atcoder::internal::inv_gcd(MOD1, MOD2).second; static constexpr long long INV_M1M2_MOD3 = atcoder::internal::inv_gcd(M1M2, MOD3).second; auto c1 = atcoder::convolution<MOD1>(a, b); auto c2 = atcoder::convolution<MOD2>(a, b); auto c3 = atcoder::convolution<MOD3>(a, b); std::vector<__uint128_t> c(n + m - 1); for (int i = 0; i < n + m - 1; ++i) { // Garner's Algorithm // X = x1 + x2 * m1 + x3 * m1 * m2 // x1 = c1[i], x2 = (c2[i] - x1) / m1 (mod m2), x3 = (c3[i] - x1 - x2 * m1) / m2 (mod m3) int x1 = c1[i]; int x2 = (atcoder::static_modint<MOD2>(c2[i] - x1) * INV_M1_MOD2).val(); int x3 = (atcoder::static_modint<MOD3>(c3[i] - x1 - x2 * MOD1) * INV_M1M2_MOD3).val(); c[i] = x1 + x2 * MOD1 + __uint128_t(x3) * M1M2; } return c; } } // namespace suisen namespace suisen { struct unsigned_bigint : private std::vector<int> { static constexpr int D = 1000000000; static constexpr int LogD = 9; static inline struct { operator unsigned_bigint() const { return unsigned_bigint{}; }; } const ZERO{}; static inline struct { operator unsigned_bigint() const { return unsigned_bigint{ 1 }; }; } const ONE{}; unsigned_bigint() : vector() {} template <typename T, std::enable_if_t<std::is_integral_v<T>, std::nullptr_t> = nullptr> unsigned_bigint(T v) { if constexpr (std::is_signed_v<T>) { assert(v >= 0); } for (; v; v /= D) { this->push_back(v % D); } } unsigned_bigint(const std::string& s) : vector() { int siz = s.size(); for (int i = siz; i > 0; i -= LogD) { int l = std::max(0, i - LogD); int r = i; int& v = this->emplace_back(); for (int j = l; j < r; ++j) { int d = s[j] - '0'; assert(0 <= d and d <= 9); v = v * 10 + d; } } } unsigned_bigint(const char* s) : unsigned_bigint(std::string(s)) {} operator bool() const { return not this->empty(); } friend bool operator==(const unsigned_bigint& a, const unsigned_bigint& b) { if (a.size() != b.size()) { return false; } for (size_t i = 0; i < a.size(); ++i) { if (a[i] != b[i]) return false; } return true; } friend bool operator!=(const unsigned_bigint& a, const unsigned_bigint& b) { return not (a == b); } friend bool operator<(const unsigned_bigint& a, const unsigned_bigint& b) { if (a.size() != b.size()) { return a.size() < b.size(); } for (size_t i = a.size(); i-- > 0;) { if (a[i] != b[i]) return a[i] < b[i]; } return false; } friend bool operator<=(const unsigned_bigint& a, const unsigned_bigint& b) { return not (b < a); } friend bool operator>(const unsigned_bigint& a, const unsigned_bigint& b) { return b < a; } friend bool operator>=(const unsigned_bigint& a, const unsigned_bigint& b) { return not (a < b); } friend unsigned_bigint& operator<<=(unsigned_bigint& a, int shamt) { if (a) a.insert(a.begin(), shamt, 0); return a; } friend unsigned_bigint operator<<(unsigned_bigint a, int shamt) { a <<= shamt; return a; } friend unsigned_bigint& operator>>=(unsigned_bigint& a, int shamt) { a.erase(a.begin(), a.begin() + std::min<int>(shamt, a.size())); return a; } friend unsigned_bigint operator>>(unsigned_bigint a, int shamt) { a >>= shamt; return a; } unsigned_bigint& operator++() { return _incr_assign(*this); } unsigned_bigint operator++(int) { unsigned_bigint res = *this; _incr_assign(*this); return res; } unsigned_bigint& operator--() { return _decr_assign(*this); } unsigned_bigint operator--(int) { unsigned_bigint res = *this; _decr_assign(*this); return res; } friend unsigned_bigint& operator+=(unsigned_bigint& a, const unsigned_bigint& b) { return _add_assign(a, b); } friend unsigned_bigint operator+(const unsigned_bigint& a, const unsigned_bigint& b) { unsigned_bigint c = a; c += b; return c; } friend unsigned_bigint& operator-=(unsigned_bigint& a, const unsigned_bigint& b) { return _sub_assign(a, b); } friend unsigned_bigint operator-(const unsigned_bigint& a, const unsigned_bigint& b) { unsigned_bigint c = a; c -= b; return c; } friend unsigned_bigint& operator*=(unsigned_bigint& a, const unsigned_bigint& b) { return a = a * b; } friend unsigned_bigint operator*(const unsigned_bigint& a, const unsigned_bigint& b) { return _mul_fft(a, b); } static std::pair<unsigned_bigint, unsigned_bigint> divmod(unsigned_bigint a, unsigned_bigint b) { return _divmod(a, b); } friend unsigned_bigint& operator/=(unsigned_bigint& a, const unsigned_bigint& b) { return a = a / b; } friend unsigned_bigint operator/(const unsigned_bigint& a, const unsigned_bigint& b) { return divmod(a, b).first; } friend unsigned_bigint& operator%=(unsigned_bigint& a, const unsigned_bigint& b) { return a = a % b; } friend unsigned_bigint operator%(const unsigned_bigint& a, const unsigned_bigint& b) { return divmod(a, b).second; } #define CAST_PRIMITIVE(type) \ operator type() const { \ type res = 0; \ for (auto it = this->rbegin(); it != this->rend(); ++it) { \ res = res * D + *it; \ } \ return res; \ } \ CAST_PRIMITIVE(unsigned int) CAST_PRIMITIVE(unsigned long) CAST_PRIMITIVE(unsigned long long) CAST_PRIMITIVE(__uint128_t) CAST_PRIMITIVE(float) CAST_PRIMITIVE(double) CAST_PRIMITIVE(long double) #undef CAST_PRIMITIVE #define CAST_SIGNED_INT(type) \ operator type() const { \ return static_cast<std::make_unsigned_t<type>>(*this); \ } \ CAST_SIGNED_INT(int) CAST_SIGNED_INT(long) CAST_SIGNED_INT(long long) #undef CAST_SIGNED_INT operator __int128_t() const { return static_cast<__uint128_t>(*this); } operator std::string() const { return _to_string(); } friend std::istream& operator>>(std::istream& in, unsigned_bigint& v) { std::string s; in >> s, v = s; return in; } friend std::ostream& operator<<(std::ostream& out, const unsigned_bigint& v) { return out << v._to_string(); } private: using std::vector<int>::vector; void _check_leading_zeros() const { assert(this->empty() or this->back()); } void _cut_leading_zeros() { while (this->size() and this->back() == 0) this->pop_back(); } static unsigned_bigint& _incr_assign(unsigned_bigint& a) { for (int& e : a) { if (++e != D) return a; e -= D; } a.push_back(1); return a; } static unsigned_bigint& _decr_assign(unsigned_bigint& a) { assert(a.size()); for (int& e : a) { if (--e >= 0) break; e += D; } a._cut_leading_zeros(); return a; } static unsigned_bigint& _add_assign(unsigned_bigint& a, const unsigned_bigint& b) { if (a.size() < b.size()) a.resize(b.size()); int carry = 0; for (size_t i = 0; i < a.size(); ++i) { if (i >= b.size()) { if (carry) { ++a[i]; } else break; } else { a[i] += b[i] + carry; } if (a[i] >= D) { a[i] -= D; carry = 1; } else { carry = 0; } } if (carry) a.push_back(1); return a; } static unsigned_bigint& _sub_assign(unsigned_bigint& a, const unsigned_bigint& b) { assert(a.size() >= b.size()); int borrow = 0; for (size_t i = 0; i < a.size(); ++i) { if (i >= b.size()) { if (borrow) { --a[i]; } else break; } else { a[i] -= b[i] + borrow; } if (a[i] < 0) { a[i] += D; borrow = 1; } else { borrow = 0; } } assert(not borrow); a._cut_leading_zeros(); return a; } unsigned_bigint _cut(int l, int r) const { r = std::min(r, static_cast<int>(this->size())); unsigned_bigint v = l < r ? unsigned_bigint(this->begin() + l, this->begin() + r) : ZERO; v._cut_leading_zeros(); return v; } template <typename T> static unsigned_bigint _build(const std::vector<T>& dat) { unsigned_bigint res; T carry = 0; for (auto v : dat) { carry += v; res.push_back(carry % D); carry /= D; } while (carry) { res.push_back(carry % D); carry /= D; } res._cut_leading_zeros(); return res; } static unsigned_bigint _mul_naive(const unsigned_bigint& a, const unsigned_bigint& b) { return _build(suisen::internal::convolution_naive<int, __int128_t>(a, b)); } static unsigned_bigint _mul_karatsuba(const unsigned_bigint& a, const unsigned_bigint& b) { if (std::min(a.size(), b.size()) <= 64) { return _mul_naive(a, b); } const int m = std::max(a.size(), b.size()) / 2; unsigned_bigint lo_a = a._cut(0, m), hi_a = a._cut(m, a.size()); unsigned_bigint lo_b = b._cut(0, m), hi_b = b._cut(m, b.size()); unsigned_bigint lo_c = lo_a * lo_b; unsigned_bigint hi_c = hi_a * hi_b; bool neg = true; unsigned_bigint md_a; if (hi_a >= lo_a) md_a = hi_a - lo_a; else md_a = lo_a - hi_a, neg = not neg; unsigned_bigint md_b; if (hi_b >= lo_b) md_b = hi_b - lo_b; else md_b = lo_b - hi_b, neg = not neg; unsigned_bigint md_ab = md_a * md_b; unsigned_bigint md_c = (hi_c << m) + lo_c + hi_c + lo_c._cut(m, lo_c.size()); if (neg) md_c -= md_ab; else md_c += md_ab; unsigned_bigint c = (md_c << m) + lo_c._cut(0, m); c._cut_leading_zeros(); return c; } static unsigned_bigint _mul_fft(const unsigned_bigint& a, const unsigned_bigint& b) { if (std::min(a.size(), b.size()) <= 64) { return _mul_naive(a, b); } if (std::max(a.size(), b.size()) <= 200) { return _mul_karatsuba(a, b); } return _build(suisen::convolution_int(a, b)); } // compare(a, D^k) static int _compare_powD(const unsigned_bigint& a, int k) { const int dA = a.size(); if (dA <= k) return -1; if (dA >= k + 2) return +1; if (a[k] != 1) return +1; for (int i = 0; i < k; ++i) { if (a[i]) return +1; } return 0; } static unsigned_bigint _powD(int k) { return ONE << k; } static std::pair<unsigned_bigint, unsigned_bigint> _divmod_int(const unsigned_bigint& a, const unsigned_bigint& b) { assert(b.size() == 1); const int b0 = b.front(); unsigned_bigint q; long long r = 0; for (auto it = a.rbegin(); it != a.rend(); ++it) { r = r * D + *it; q.push_back(r / b0); r %= b0; } std::reverse(q.begin(), q.end()); q._cut_leading_zeros(); return { q, r ? unsigned_bigint{ int(r) } : ZERO }; } static std::pair<unsigned_bigint, unsigned_bigint> _divmod_naive(unsigned_bigint& a, unsigned_bigint& b) { if (b.size() == 1) { return _divmod_int(a, b); } if (a < b) { return { ZERO, a }; } const unsigned_bigint coef{ D / (b.back() + 1) }; a *= coef; b *= coef; assert(2 * b.back() >= D); unsigned_bigint q, r(a.end() - b.size(), a.end()); for (int i = a.size() - b.size(); i >= 0; --i) { if (r.size() < b.size()) { q.push_back(0); } else if (r.size() == b.size()) { if (r >= b) { q.push_back(1); r -= b; } else { q.push_back(0); } } else { assert(r.size() == b.size() + 1); int x = (static_cast<long long>(r.end()[-1]) * D + r.end()[-2]) / b.back(); unsigned_bigint bx = b * unsigned_bigint{ x }; while (bx > r) bx -= b, --x; while (bx + b <= r) bx += b, ++x; q.push_back(x); r = r - bx; } if (i) { r.insert(r.begin(), a[i - 1]); } } std::reverse(q.begin(), q.end()); q._cut_leading_zeros(); r._cut_leading_zeros(); return { q, _divmod_int(r, coef).first }; } static std::pair<unsigned_bigint, unsigned_bigint> _divmod_naive(const unsigned_bigint& a, const unsigned_bigint& b) { unsigned_bigint a_ = a, b_ = b; return _divmod_naive(a_, b_); } // floor(D^n/b) static unsigned_bigint _div_powD(int n, unsigned_bigint b) { assert(b.size() and 2 * b.back() >= D); const int dB = b.size(); int k = n - dB; while (k > 64) k = (k + 1) >> 1; k = std::min(n, dB + k); unsigned_bigint c = _divmod_naive(_powD(k), b).first; unsigned_bigint bc = b * c; for (; k < n; k = 2 * k - dB) { // loop invariant: c = floor(D^k/B) bc.resize(k + 1); int carry = 0; for (int i = 0; i < k; ++i) { bc[i] = D - bc[i] + (i ? carry - 1 : 0); if (bc[i] >= D) { bc[i] -= D; carry = 1; } else { carry = 0; } } ++bc[k]; c *= bc; c.erase(c.begin(), c.begin() + dB); bc = b * c; if (_compare_powD(bc + b, 2 * k - dB) <= 0) { ++c, bc += b; } assert(_compare_powD(bc + b, 2 * k - dB) > 0); } // c = floor(D^k/b) c >>= k - n; return c; } static std::pair<unsigned_bigint, unsigned_bigint> _divmod(unsigned_bigint a, unsigned_bigint b) { assert(b.size()); if (std::min(static_cast<int>(b.size()), static_cast<int>(a.size()) - static_cast<int>(b.size())) <= 64) { return _divmod_naive(a, b); } if (a < b) { return { ZERO, a }; } const unsigned_bigint coef{ D / (b.back() + 1) }; a *= coef; b *= coef; assert(2 * b.back() >= D); const int dA = a.size(), dB = b.size(); if (dA == dB) { return { ONE, _divmod_int(a - b, coef).first }; } unsigned_bigint invB = _div_powD(dA, b); unsigned_bigint q = a * invB; q.erase(q.begin(), q.begin() + dA); unsigned_bigint qb = q * b, qb2 = qb + b; if (qb2 <= a) { return { ++q, _divmod_int(a - qb2, coef).first }; } else { return { q, _divmod_int(a - qb, coef).first }; } } std::string _to_string() const { if (this->empty()) return "0"; std::ostringstream oss; auto it = this->rbegin(); oss << *it; while (++it != this->rend()) { oss << std::setw(9) << std::setfill('0') << *it; } return oss.str(); } }; } // namespace suisen namespace suisen { struct bigint { static inline struct { operator bigint() const { return bigint{ unsigned_bigint::ZERO }; }; } const ZERO{}; static inline struct { operator bigint() const { return bigint{ unsigned_bigint::ONE }; }; } const ONE{}; bigint() : _neg(false), _dat{} {} template <typename T, std::enable_if_t<std::is_integral_v<T>, std::nullptr_t> = nullptr> bigint(T v) { _neg = false; if constexpr (std::is_signed_v<T>) { if (v < 0) { _neg = true; v = -v; } } _dat = unsigned_bigint(v); } bigint(const std::string& s) { if (s.front() == '-') { _neg = true; _dat = unsigned_bigint(s.substr(1)); fix_sign(); } else { _neg = false; _dat = unsigned_bigint(s); } } bigint(const char* s) : bigint(std::string(s)) {} bigint(const unsigned_bigint& dat) : _neg(false), _dat(dat) {} bigint(unsigned_bigint&& dat) : _neg(false), _dat(std::move(dat)) {} operator bool() const { return bool(_dat); } friend bool operator==(const bigint& a, const bigint& b) { if (a._neg xor b._neg) { return false; } else { return a._dat == b._dat; } } friend bool operator!=(const bigint& a, const bigint& b) { return not (a == b); } friend bool operator<(const bigint& a, const bigint& b) { if (a._neg xor b._neg) { return a._neg; } else if (a._neg) { return a._dat > b._dat; } else { return a._dat < b._dat; } } friend bool operator<=(const bigint& a, const bigint& b) { return not (b < a); } friend bool operator>(const bigint& a, const bigint& b) { return b < a; } friend bool operator>=(const bigint& a, const bigint& b) { return not (a < b); } friend bigint& operator<<=(bigint& a, int shamt) { a._dat <<= shamt; return a; } friend bigint operator<<(bigint a, int shamt) { a <<= shamt; return a; } friend bigint& operator>>=(bigint& a, int shamt) { a._dat >>= shamt; a.fix_sign(); return a; } friend bigint operator>>(bigint a, int shamt) { a >>= shamt; a.fix_sign(); return a; } bigint operator+() const { return *this; } bigint operator-() const { return bigint(_dat, not _neg); } bigint& operator++() { if (_neg) { --_dat; fix_sign(); } else { ++_dat; } return *this; } bigint operator++(int) { bigint res = *this; ++*this; return res; } bigint& operator--() { if (_neg) { ++_dat; } else if (_dat) { --_dat; } else { _neg = true; _dat = unsigned_bigint::ONE; } return *this; } bigint operator--(int) { bigint res = *this; --*this; return res; } friend bigint& operator+=(bigint& a, const bigint& b) { if (a._neg xor b._neg) { if (a._dat >= b._dat) { a._dat -= b._dat; } else { a._dat = b._dat - a._dat; a._neg = not a._neg; } a.fix_sign(); } else { a._dat += b._dat; } return a; } friend bigint operator+(const bigint& a, const bigint& b) { bigint c = a; c += b; return c; } friend bigint& operator-=(bigint& a, const bigint& b) { if (a._neg xor b._neg) { a._dat += b._dat; } else { if (a._dat >= b._dat) { a._dat -= b._dat; } else { a._dat = b._dat - a._dat; a._neg = not a._neg; } a.fix_sign(); } return a; } friend bigint operator-(const bigint& a, const bigint& b) { bigint c = a; c -= b; return c; } friend bigint& operator*=(bigint& a, const bigint& b) { return a = a * b; } friend bigint operator*(const bigint& a, const bigint& b) { return bigint(a._dat * b._dat, a._neg xor b._neg); } static std::pair<bigint, bigint> divmod(bigint a, bigint b) { auto [q, r] = unsigned_bigint::divmod(a._dat, b._dat); return { bigint(std::move(q), a._neg xor b._neg), bigint(std::move(r), a._neg) }; } friend bigint& operator/=(bigint& a, const bigint& b) { return a = a / b; } friend bigint operator/(const bigint& a, const bigint& b) { return divmod(a, b).first; } friend bigint& operator%=(bigint& a, const bigint& b) { return a = a % b; } friend bigint operator%(const bigint& a, const bigint& b) { return divmod(a, b).second; } #define CAST_PRIMITIVE(type) \ operator type() const { \ type res = _dat; \ return _neg ? -res : res; \ } \ CAST_PRIMITIVE(unsigned int) CAST_PRIMITIVE(unsigned long) CAST_PRIMITIVE(unsigned long long) CAST_PRIMITIVE(__uint128_t) CAST_PRIMITIVE(float) CAST_PRIMITIVE(double) CAST_PRIMITIVE(long double) #undef CAST_PRIMITIVE #define CAST_SIGNED_INT(type) \ operator type() const { \ return static_cast<std::make_unsigned_t<type>>(*this); \ } \ CAST_SIGNED_INT(int) CAST_SIGNED_INT(long) CAST_SIGNED_INT(long long) #undef CAST_SIGNED_INT operator __int128_t() const { return static_cast<__uint128_t>(*this); } operator unsigned_bigint() const { assert(not _neg); return _dat; } operator std::string() const { if (_neg) { return '-' + std::string(_dat); } else { return std::string(_dat); } } friend std::istream& operator>>(std::istream& in, bigint& v) { std::string s; in >> s, v = s; return in; } friend std::ostream& operator<<(std::ostream& out, const bigint& v) { return out << std::string(v); } private: bigint(const unsigned_bigint& dat, bool neg) : _neg(neg), _dat(dat) { fix_sign(); } bigint(unsigned_bigint&& dat, bool neg) : _neg(neg), _dat(std::move(dat)) { fix_sign(); } bool _neg; unsigned_bigint _dat; void fix_sign() { if (_neg and not _dat) _neg = false; } }; } // namespace suisen #include <cassert> namespace suisen { template <typename T, typename U = T> struct factorial { factorial() = default; factorial(int n) { ensure(n); } static void ensure(const int n) { int sz = _fac.size(); if (n + 1 <= sz) return; int new_size = std::max(n + 1, sz * 2); _fac.resize(new_size), _fac_inv.resize(new_size); for (int i = sz; i < new_size; ++i) _fac[i] = _fac[i - 1] * i; _fac_inv[new_size - 1] = U(1) / _fac[new_size - 1]; for (int i = new_size - 1; i > sz; --i) _fac_inv[i - 1] = _fac_inv[i] * i; } T fac(const int i) { ensure(i); return _fac[i]; } T operator()(int i) { return fac(i); } U fac_inv(const int i) { ensure(i); return _fac_inv[i]; } U binom(const int n, const int r) { if (n < 0 or r < 0 or n < r) return 0; ensure(n); return _fac[n] * _fac_inv[r] * _fac_inv[n - r]; } template <typename ...Ds, std::enable_if_t<std::conjunction_v<std::is_integral<Ds>...>, std::nullptr_t> = nullptr> U polynom(const int n, const Ds& ...ds) { if (n < 0) return 0; ensure(n); int sumd = 0; U res = _fac[n]; for (int d : { ds... }) { if (d < 0 or d > n) return 0; sumd += d; res *= _fac_inv[d]; } if (sumd > n) return 0; res *= _fac_inv[n - sumd]; return res; } U perm(const int n, const int r) { if (n < 0 or r < 0 or n < r) return 0; ensure(n); return _fac[n] * _fac_inv[n - r]; } private: static std::vector<T> _fac; static std::vector<U> _fac_inv; }; template <typename T, typename U> std::vector<T> factorial<T, U>::_fac{ 1 }; template <typename T, typename U> std::vector<U> factorial<T, U>::_fac_inv{ 1 }; } // namespace suisen using suisen::bigint; mint solve(int N, std::vector<bigint>& A, bigint M) { suisen::factorial<mint> fact(10000000); std::vector<bigint> Mt(N + 1); Mt[N] = M; for (int i = N - 1; i >= 0; --i) { Mt[i] = Mt[i + 1] / A[i]; } std::vector<mint> pd(N + 1); for (int x = 0; x <= N; ++x) { auto binom = [](bigint n, int r) -> mint { if (r < 0 or bigint(r) > n) return 0; mint n2 = (int) bigint::divmod(n, mint::mod()).second; mint num = 1, den = 1; for (int i = 0; i < r; ++i) { num *= n2 - i; den *= 1 + i; } return num / den; }; pd[x] = binom(bigint(N) + (Mt[0] - bigint(x)), N); } std::vector<int> fac(5000000), ifac(5000000); for (int i = 0; i < 5000000; ++i) { fac[i] = fact.fac(i).val(); ifac[i] = fact.fac_inv(i).val(); } for (int t = 1; t <= N; ++t) { const int At = A[t - 1]; // x_i,...,x_n < At const int u = N - t; const int Rt = Mt[t] % bigint(At); std::vector<mint> a(N + 1); for (int i = 0; i <= N; ++i) { a[i] = (i & 1 ? -1 : 1) * fact.binom(u + 1, i); } std::reverse(pd.begin(), pd.end()); std::vector<mint> f = atcoder::convolution(a, pd); f.resize(N + 1); std::reverse(f.begin(), f.end()); std::vector<mint> dp(N + 1); for (int i = 0; i <= N; ++i) { const int T = At * i + Rt; const int maxk = std::min(N, T); const mint fi = f[i]; for (int k = 0; k <= maxk; ++k) { dp[k] += (long long) (fac[T + u - k]) * ifac[T - k] * fi; } } for (int k = 0; k <= N; ++k) { dp[k] *= ifac[u]; } pd.swap(dp); } return pd[0]; } int main() { int N; std::cin >> N; --N; std::vector<bigint> A(N); for (auto& e : A) std::cin >> e; std::reverse(A.begin(), A.end()); bigint M; std::cin >> M; std::cout << solve(N, A, M).val() << std::endl; }