結果
問題 | No.2580 Hyperinflation |
ユーザー | suisen |
提出日時 | 2023-12-08 00:28:03 |
言語 | C++23 (gcc 12.3.0 + boost 1.83.0) |
結果 |
TLE
|
実行時間 | - |
コード長 | 39,095 bytes |
コンパイル時間 | 5,297 ms |
コンパイル使用メモリ | 227,796 KB |
実行使用メモリ | 13,056 KB |
最終ジャッジ日時 | 2024-09-27 02:29:08 |
合計ジャッジ時間 | 11,674 ms |
ジャッジサーバーID (参考情報) |
judge2 / judge1 |
(要ログイン)
テストケース
テストケース表示入力 | 結果 | 実行時間 実行使用メモリ |
---|---|---|
testcase_00 | AC | 2 ms
5,248 KB |
testcase_01 | AC | 2 ms
5,248 KB |
testcase_02 | AC | 2 ms
5,248 KB |
testcase_03 | AC | 2 ms
5,376 KB |
testcase_04 | AC | 2 ms
5,376 KB |
testcase_05 | AC | 2 ms
5,376 KB |
testcase_06 | AC | 2 ms
5,376 KB |
testcase_07 | AC | 2 ms
5,376 KB |
testcase_08 | AC | 2 ms
5,376 KB |
testcase_09 | AC | 2 ms
5,376 KB |
testcase_10 | AC | 2 ms
5,376 KB |
testcase_11 | AC | 2 ms
5,376 KB |
testcase_12 | AC | 2 ms
5,376 KB |
testcase_13 | AC | 15 ms
5,376 KB |
testcase_14 | AC | 160 ms
5,376 KB |
testcase_15 | AC | 60 ms
5,376 KB |
testcase_16 | AC | 381 ms
5,376 KB |
testcase_17 | AC | 2 ms
5,376 KB |
testcase_18 | TLE | - |
testcase_19 | -- | - |
testcase_20 | -- | - |
testcase_21 | -- | - |
testcase_22 | -- | - |
testcase_23 | -- | - |
testcase_24 | -- | - |
testcase_25 | -- | - |
testcase_26 | -- | - |
testcase_27 | -- | - |
testcase_28 | -- | - |
testcase_29 | -- | - |
testcase_30 | -- | - |
testcase_31 | -- | - |
testcase_32 | -- | - |
testcase_33 | -- | - |
ソースコード
#include <bit> #include <iostream> #include <vector> #include <atcoder/modint> #include <atcoder/convolution> using mint = atcoder::modint998244353; #include <cassert> #include <vector> namespace suisen { template <typename T, typename SumType, SumType(*e)(), SumType(*append)(SumType, T), typename R, R(*merge)(SumType, SumType)> struct QueueAggregation { using value_type = T; using pointer = T*; using const_pointer = const T*; using reference = T&; using const_reference = const T&; using sum_type = SumType; private: struct QueueAggregationIterator { using difference_type = int; using value_type = T; using pointer = const_pointer; using reference = const_reference; using iterator_category = std::random_access_iterator_tag; private: using fi_iterator_type = typename std::vector<std::pair<value_type, value_type>>::const_reverse_iterator; using se_iterator_type = typename std::vector<std::pair<value_type, value_type>>::const_iterator; public: fi_iterator_type it_l; fi_iterator_type it_l_end; se_iterator_type it_r_begin; se_iterator_type it_r; QueueAggregationIterator& operator++() { if (it_l == it_l_end) ++it_r; else ++it_l; return *this; } QueueAggregationIterator operator++(int) { QueueAggregationIterator ret = *this; ++(*this); return ret; } QueueAggregationIterator& operator--() { if (it_r == it_r_begin) --it_l; else --it_r; return *this; } QueueAggregationIterator operator--(int) { QueueAggregationIterator ret = *this; --(*this); return ret; } QueueAggregationIterator& operator+=(difference_type dif) { if (dif < 0) return *this -= -dif; if (int d = it_l_end - it_l; d < dif) it_l = it_l_end, it_r += dif - d; else it_l += dif; return *this; } friend QueueAggregationIterator operator+(QueueAggregationIterator it, difference_type dif) { it += dif; return it; } friend QueueAggregationIterator operator+(difference_type dif, QueueAggregationIterator it) { it += dif; return it; } QueueAggregationIterator& operator-=(difference_type dif) { if (dif < 0) return *this += -dif; if (int d = it_r - it_r_begin; d < dif) it_r = it_r_begin, it_l -= dif - d; else it_r -= dif; return *this; } friend QueueAggregationIterator operator-(QueueAggregationIterator it, difference_type dif) { it -= dif; return it; } difference_type operator-(const QueueAggregationIterator& rhs) const { difference_type d1 = it_l == it_l_end ? it_r - it_r_begin : it_l - it_l_end; difference_type d2 = rhs.it_l == rhs.it_l_end ? rhs.it_r - rhs.it_r_begin : rhs.it_l - rhs.it_l_end; return d1 - d2; } reference operator[](difference_type i) const { return *((*this) + i); } reference operator*() const { return it_l == it_l_end ? it_r->first : it_l->first; } bool operator!=(const QueueAggregationIterator& rhs) const { return it_l != rhs.it_l or it_r != rhs.it_r; } bool operator==(const QueueAggregationIterator& rhs) const { return not (*this != rhs); } bool operator< (const QueueAggregationIterator& rhs) const { return (*this) - rhs < 0; } bool operator<=(const QueueAggregationIterator& rhs) const { return (*this) - rhs <= 0; } bool operator> (const QueueAggregationIterator& rhs) const { return (*this) - rhs > 0; } bool operator>=(const QueueAggregationIterator& rhs) const { return (*this) - rhs >= 0; } }; public: using iterator = QueueAggregationIterator; using const_iterator = iterator; QueueAggregation() = default; template <typename InputIterator, std::enable_if_t<std::is_constructible_v<value_type, typename InputIterator::value_type>, std::nullptr_t> = nullptr> QueueAggregation(InputIterator first, InputIterator last) { for (; first != last; ++first) push_back(*first); } template <typename Container, std::enable_if_t<std::is_constructible_v<value_type, typename Container::value_type>, std::nullptr_t> = nullptr> QueueAggregation(const Container& c): QueueAggregation(std::begin(c), std::end(c)) {} R prod() const { return merge(prod(_st_l), prod(_st_r)); } void push_back(const value_type& val) { _st_r.emplace_back(val, append(prod(_st_r), val)); } void pop_front() { if (_st_l.size()) return _st_l.pop_back(); const int siz = _st_r.size(); assert(siz); for (int i = siz - 1; i > 0; --i) { value_type v = std::move(_st_r[i].first); _st_l.emplace_back(v, append(prod(_st_l), v)); } _st_r.clear(); } const value_type& front() const { return _st_l.size() ? _st_l.back().first : _st_r.front().first; } const value_type& back() const { return _st_r.size() ? _st_r.back().first : _st_l.front().first; } const value_type& operator[](int i) const { const int k = i - _st_l.size(); return k < 0 ? _st_l[~k].first : _st_r[k].first; } int size() const { return _st_l.size() + _st_r.size(); } void clear() { _st_l.clear(), _st_r.clear(); } void shrink_to_fit() { _st_l.shrink_to_fit(), _st_r.shrink_to_fit(); } iterator begin() const { return iterator{ _st_l.rbegin(), _st_l.rend(), _st_r.begin(), _st_r.begin() }; } iterator end() const { return iterator{ _st_l.rend(), _st_l.rend(), _st_r.begin(), _st_r.end() }; } iterator cbegin() const { return begin(); } iterator cend() const { return end(); } private: std::vector<std::pair<value_type, sum_type>> _st_l, _st_r; sum_type prod(const std::vector<std::pair<value_type, sum_type>>& st) const { return st.empty() ? e() : st.back().second; } }; } // namespace suisen using SumType = mint; using ElmType = mint; using RetType = mint; SumType e() { return 1; } SumType append(SumType x, ElmType y) { return x * y; } RetType merge(SumType x, SumType y) { return x * y; } using BinomSWAG = suisen::QueueAggregation<ElmType, SumType, e, append, RetType, merge>; #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 using suisen::bigint; mint solve(int N, std::vector<bigint> &A, bigint M) { 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 binom_table(N + 2, std::vector<mint>(N + 2)); for (int i = 0; i <= N + 1; ++i) { binom_table[i][0] = 1; for (int j = 1; j <= i; ++j) { binom_table[i][j] = binom_table[i - 1][j - 1] + binom_table[i - 1][j]; } } 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 + 1) + (Mt[0] - bigint(x)), N + 1); } const int z = 1 << std::bit_width<unsigned>(2 * N); const mint iz = mint(z).inv(); std::vector bs(N + 1, std::vector<mint>(N + 1)); for (int t = 1; t <= N; ++t) if (const bigint At = A[t - 1]; At != bigint(1)) { // x_i,...,x_n < At const int u = N - t + 1; const bigint Rt = Mt[t] % At; std::vector<mint> dp(N + 1); // 60 * N * N * log N mint fac_t_inv = 1; for (int t = 1; t <= u; ++t) fac_t_inv *= t; fac_t_inv = fac_t_inv.inv(); for (int i = 0; i <= N; ++i) { // (R_t-x+kA_t)+u ... (R_t-x+kA_t)+1 // init: (Rt+kr)+u ... (Rt+kr)+1 BinomSWAG binom; for (bigint v = Rt + bigint(i) * At + bigint(u); v >= Rt + bigint(i) * At + bigint(1); --v) { binom.push_back(int(v % bigint(mint::mod()))); } for (int d = 0; d <= N; ++d) { bs[d][i] = binom.prod() * fac_t_inv; mint nxt_back = binom.back() - 1; binom.push_back(nxt_back); binom.pop_front(); } } std::vector<mint> a(z); for (int i = 0; i <= N; ++i) { a[i] = (i & 1 ? -1 : 1) * binom_table[u + 1][i]; } atcoder::internal::butterfly(a); for (int x = 0; x <= N; ++x) { bs[x].resize(z); atcoder::internal::butterfly(bs[x]); for (int t = 0; t < z; ++t) bs[x][t] *= a[t] * iz; atcoder::internal::butterfly_inv(bs[x]); for (int y = 0; y <= N; ++y) dp[x] += bs[x][y] * pd[y]; bs[x].resize(N + 1); } 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) - solve(N, A, M - bigint(1))).val() << std::endl; }