// https://atcoder.jp/contests/arc118/submissions/24453032 #include #include using Fp = atcoder::modint998244353; std::ostream& operator<<(std::ostream& os, Fp a) { return os << a.val(); } using Fps = std::vector; int sz(const Fps& a) { return a.size(); } Fps operator-(Fps a) { for (auto&& e : a) e = -e; return a; } Fps& operator+=(Fps& a, const Fps& b) { if (sz(a) < sz(b)) a.reserve(sz(b)), a.resize(sz(b)); for (int i = 0; i < sz(b); ++i) a[i] += b[i]; return a; } Fps operator+(Fps a, const Fps& b) { return std::move(a += b); } Fps& operator-=(Fps& a, const Fps& b) { if (sz(a) < sz(b)) a.reserve(sz(b)), a.resize(sz(b)); for (int i = 0; i < sz(b); ++i) a[i] -= b[i]; return a; } Fps operator-(Fps a, const Fps& b) { return std::move(a -= b); } Fps& operator*=(Fps& a, Fp b) { for (auto&& e : a) e *= b; return a; } Fps operator*(Fps a, Fp b) { return std::move(a *= b); } Fps operator*(Fp a, Fps b) { return std::move(b *= a); } Fps& operator/=(Fps& a, Fp b) { b = b.inv(); for (auto&& e : a) e *= b; return a; } Fps operator/(Fps a, Fp b) { return std::move(a /= b); } Fps fft(const Fps& a, int n) { Fps res(n); std::copy(a.begin(), a.begin() + std::min(n, sz(a)), res.begin()); atcoder::internal::butterfly(res); return res; } Fps circ(Fps&& a, const Fps& b) { if (sz(a) < sz(b)) a.reserve(sz(b)), a.resize(sz(b)); for (int i = 0; i < sz(b); ++i) a[i] *= b[i]; return a; } Fps circ(Fps&& a) { for (auto&& e : a) e *= e; return a; } Fps ifft(Fps&& a, int size) { int n = sz(a); atcoder::internal::butterfly_inv(a); a.resize(size); a *= (1 - Fp::mod()) / n; return a; } Fps operator*(const Fps& a, const Fps& b) { if (a.empty() || b.empty()) return {}; if (std::min(sz(a), sz(b)) <= 1) { Fps res(std::max(sz(a), sz(b))); for (int i = 0; i < sz(a); ++i) for (int j = 0; j < sz(b); ++j) { if (i + j == sz(res)) break; res[i + j] += a[i] * b[j]; } return res; } int n = 1 << atcoder::internal::bit_ceil(sz(a) + sz(b) - 1); auto buf = fft(a, n); if (&a == &b) buf = circ(std::move(buf)); else buf = circ(std::move(buf), fft(b, n)); return ifft(std::move(buf), std::max(sz(a), sz(b))); } Fps& operator*=(Fps& a, const Fps& b) { return a = a * b; } Fps inv(const Fps& a) { Fps res{a[0].inv()}; for (int n = 1; n < sz(a); n *= 2) { auto f_res = fft(res, 2 * n); Fps buf = ifft(circ(fft(a, 2 * n), f_res), 2 * n); std::fill(buf.begin(), buf.begin() + n, 0); buf = ifft(circ(fft(buf, 2 * n), f_res), std::min(2 * n, sz(a))); for (int i = n; i < sz(buf); ++i) res.push_back(-buf[i]); } return res; } using Poly = std::vector; Fp eval(const Poly& a, Fp x) { Fp res; for (int i = sz(a); i--;) (res *= x) += a[i]; return res; } std::vector fact, ifact; void reserve(int n) { fact.resize(n + 1), ifact.resize(n + 1); fact[0] = 1; for (int i = 1; i <= n; ++i) fact[i] = i * fact[i - 1]; ifact[n] = fact[n].pow(Fp::mod() - 2); for (int i = n; i; --i) ifact[i - 1] = ifact[i] * i; } using namespace std; using cpx = complex; const double PI = acos(-1); vector roots = {{0, 0}, {1, 0}}; void ensure_capacity(int min_capacity) { for (int len = roots.size(); len < min_capacity; len *= 2) { for (int i = len >> 1; i < len; i++) { roots.emplace_back(roots[i]); double angle = 2 * PI * (2 * i + 1 - len) / (len * 2); roots.emplace_back(cos(angle), sin(angle)); } } } void fft(vector &z, bool inverse) { int n = z.size(); assert((n & (n - 1)) == 0); ensure_capacity(n); for (unsigned i = 1, j = 0; i < n; i++) { int bit = n >> 1; for (; j >= bit; bit >>= 1) j -= bit; j += bit; if (i < j) swap(z[i], z[j]); } for (int len = 1; len < n; len <<= 1) { for (int i = 0; i < n; i += len * 2) { for (int j = 0; j < len; j++) { cpx root = inverse ? conj(roots[j + len]) : roots[j + len]; cpx u = z[i + j]; cpx v = z[i + j + len] * root; z[i + j] = u + v; z[i + j + len] = u - v; } } } if (inverse) for (int i = 0; i < n; i++) z[i] /= n; } vector multiply_bigint(const vector &a, const vector &b, int base) { int need = a.size() + b.size(); int n = 1; while (n < need) n <<= 1; vector p(n); for (size_t i = 0; i < n; i++) { p[i] = cpx(i < a.size() ? a[i] : 0, i < b.size() ? b[i] : 0); } fft(p, false); // a[w[k]] = (p[w[k]] + conj(p[w[n-k]])) / 2 // b[w[k]] = (p[w[k]] - conj(p[w[n-k]])) / (2*i) vector ab(n); cpx r(0, -0.25); for (int i = 0; i < n; i++) { int j = (n - i) & (n - 1); ab[i] = (p[i] * p[i] - conj(p[j] * p[j])) * r; } fft(ab, true); vector result(need); long long carry = 0; for (int i = 0; i < need; i++) { long long d = (long long) (ab[i].real() + 0.5) + carry; carry = d / base; result[i] = d % base; } return result; } vector multiply_mod(const vector &a, const vector &b, int m) { int need = a.size() + b.size() - 1; int n = 1; while (n < need) n <<= 1; vector A(n); for (size_t i = 0; i < a.size(); i++) { int x = (a[i] % m + m) % m; A[i] = cpx(x & ((1 << 15) - 1), x >> 15); } fft(A, false); vector B(n); for (size_t i = 0; i < b.size(); i++) { int x = (b[i] % m + m) % m; B[i] = cpx(x & ((1 << 15) - 1), x >> 15); } fft(B, false); vector fa(n); vector fb(n); for (int i = 0, j = 0; i < n; i++, j = n - i) { cpx a1 = (A[i] + conj(A[j])) * cpx(0.5, 0); cpx a2 = (A[i] - conj(A[j])) * cpx(0, -0.5); cpx b1 = (B[i] + conj(B[j])) * cpx(0.5, 0); cpx b2 = (B[i] - conj(B[j])) * cpx(0, -0.5); fa[i] = a1 * b1 + a2 * b2 * cpx(0, 1); fb[i] = a1 * b2 + a2 * b1; } fft(fa, true); fft(fb, true); vector res(need); for (int i = 0; i < need; i++) { long long aa = (long long) (fa[i].real() + 0.5); long long bb = (long long) (fb[i].real() + 0.5); long long cc = (long long) (fa[i].imag() + 0.5); res[i] = (aa % m + (bb % m << 15) + (cc % m << 30)) % m; } return res; } constexpr int digits(int base) noexcept { return base <= 1 ? 0 : 1 + digits(base / 10); } constexpr int base = 1000'000'000; constexpr int base_digits = digits(base); constexpr int fft_base = 10'000; // fft_base^2 * n / fft_base_digits <= 10^15 for double constexpr int fft_base_digits = digits(fft_base); struct bigint { // value == 0 is represented by empty z vector z; // digits // sign == 1 <==> value >= 0 // sign == -1 <==> value < 0 int sign; bigint(long long v = 0) { *this = v; } bigint &operator=(long long v) { sign = v < 0 ? -1 : 1; v *= sign; z.clear(); for (; v > 0; v = v / base) z.push_back((int) (v % base)); return *this; } bigint(const string &s) { read(s); } bigint &operator+=(const bigint &other) { if (sign == other.sign) { for (int i = 0, carry = 0; i < other.z.size() || carry; ++i) { if (i == z.size()) z.push_back(0); z[i] += carry + (i < other.z.size() ? other.z[i] : 0); carry = z[i] >= base; if (carry) z[i] -= base; } } else if (other != 0 /* prevent infinite loop */) { *this -= -other; } return *this; } friend bigint operator+(bigint a, const bigint &b) { a += b; return a; } bigint &operator-=(const bigint &other) { if (sign == other.sign) { if ((sign == 1 && *this >= other) || (sign == -1 && *this <= other)) { for (int i = 0, carry = 0; i < other.z.size() || carry; ++i) { z[i] -= carry + (i < other.z.size() ? other.z[i] : 0); carry = z[i] < 0; if (carry) z[i] += base; } trim(); } else { *this = other - *this; this->sign = -this->sign; } } else { *this += -other; } return *this; } friend bigint operator-(bigint a, const bigint &b) { a -= b; return a; } bigint &operator*=(int v) { if (v < 0) sign = -sign, v = -v; for (int i = 0, carry = 0; i < z.size() || carry; ++i) { if (i == z.size()) z.push_back(0); long long cur = (long long) z[i] * v + carry; carry = (int) (cur / base); z[i] = (int) (cur % base); } trim(); return *this; } bigint operator*(int v) const { return bigint(*this) *= v; } friend pair divmod(const bigint &a1, const bigint &b1) { int norm = base / (b1.z.back() + 1); bigint a = a1.abs() * norm; bigint b = b1.abs() * norm; bigint q, r; q.z.resize(a.z.size()); for (int i = (int) a.z.size() - 1; i >= 0; i--) { r *= base; r += a.z[i]; int s1 = b.z.size() < r.z.size() ? r.z[b.z.size()] : 0; int s2 = b.z.size() - 1 < r.z.size() ? r.z[b.z.size() - 1] : 0; int d = (int) (((long long) s1 * base + s2) / b.z.back()); r -= b * d; while (r < 0) r += b, --d; q.z[i] = d; } q.sign = a1.sign * b1.sign; r.sign = a1.sign; q.trim(); r.trim(); return {q, r / norm}; } friend bigint sqrt(const bigint &a1) { bigint a = a1; while (a.z.empty() || a.z.size() % 2 == 1) a.z.push_back(0); int n = a.z.size(); int firstDigit = (int) ::sqrt((double) a.z[n - 1] * base + a.z[n - 2]); int norm = base / (firstDigit + 1); a *= norm; a *= norm; while (a.z.empty() || a.z.size() % 2 == 1) a.z.push_back(0); bigint r = (long long) a.z[n - 1] * base + a.z[n - 2]; firstDigit = (int) ::sqrt((double) a.z[n - 1] * base + a.z[n - 2]); int q = firstDigit; bigint res; for (int j = n / 2 - 1; j >= 0; j--) { for (;; --q) { bigint r1 = (r - (res * 2 * base + q) * q) * base * base + (j > 0 ? (long long) a.z[2 * j - 1] * base + a.z[2 * j - 2] : 0); if (r1 >= 0) { r = r1; break; } } res *= base; res += q; if (j > 0) { int d1 = res.z.size() + 2 < r.z.size() ? r.z[res.z.size() + 2] : 0; int d2 = res.z.size() + 1 < r.z.size() ? r.z[res.z.size() + 1] : 0; int d3 = res.z.size() < r.z.size() ? r.z[res.z.size()] : 0; q = (int) (((long long) d1 * base * base + (long long) d2 * base + d3) / (firstDigit * 2)); } } res.trim(); return res / norm; } bigint operator/(const bigint &v) const { return divmod(*this, v).first; } bigint operator%(const bigint &v) const { return divmod(*this, v).second; } bigint &operator/=(int v) { if (v < 0) sign = -sign, v = -v; for (int i = (int) z.size() - 1, rem = 0; i >= 0; --i) { long long cur = z[i] + rem * (long long) base; z[i] = (int) (cur / v); rem = (int) (cur % v); } trim(); return *this; } bigint operator/(int v) const { return bigint(*this) /= v; } int operator%(int v) const { if (v < 0) v = -v; int m = 0; for (int i = (int) z.size() - 1; i >= 0; --i) m = (int) ((z[i] + m * (long long) base) % v); return m * sign; } bigint &operator*=(const bigint &v) { *this = *this * v; return *this; } bigint &operator/=(const bigint &v) { *this = *this / v; return *this; } bigint &operator%=(const bigint &v) { *this = *this % v; return *this; } bool operator<(const bigint &v) const { if (sign != v.sign) return sign < v.sign; if (z.size() != v.z.size()) return z.size() * sign < v.z.size() * v.sign; for (int i = (int) z.size() - 1; i >= 0; i--) if (z[i] != v.z[i]) return z[i] * sign < v.z[i] * sign; return false; } bool operator>(const bigint &v) const { return v < *this; } bool operator<=(const bigint &v) const { return !(v < *this); } bool operator>=(const bigint &v) const { return !(*this < v); } bool operator==(const bigint &v) const { return !(*this < v) && !(v < *this); } bool operator!=(const bigint &v) const { return *this < v || v < *this; } void trim() { while (!z.empty() && z.back() == 0) z.pop_back(); if (z.empty()) sign = 1; } bool isZero() const { return z.empty(); } friend bigint operator-(bigint v) { if (!v.z.empty()) v.sign = -v.sign; return v; } bigint abs() const { return sign == 1 ? *this : -*this; } long long longValue() const { long long res = 0; for (int i = (int) z.size() - 1; i >= 0; i--) res = res * base + z[i]; return res * sign; } friend bigint gcd(const bigint &a, const bigint &b) { return b.isZero() ? a : gcd(b, a % b); } friend bigint lcm(const bigint &a, const bigint &b) { return a / gcd(a, b) * b; } void read(const string &s) { sign = 1; z.clear(); int pos = 0; while (pos < s.size() && (s[pos] == '-' || s[pos] == '+')) { if (s[pos] == '-') sign = -sign; ++pos; } for (int i = (int) s.size() - 1; i >= pos; i -= base_digits) { int x = 0; for (int j = max(pos, i - base_digits + 1); j <= i; j++) x = x * 10 + s[j] - '0'; z.push_back(x); } trim(); } friend istream &operator>>(istream &stream, bigint &v) { string s; stream >> s; v.read(s); return stream; } friend ostream &operator<<(ostream &stream, const bigint &v) { if (v.sign == -1) stream << '-'; stream << (v.z.empty() ? 0 : v.z.back()); for (int i = (int) v.z.size() - 2; i >= 0; --i) stream << setw(base_digits) << setfill('0') << v.z[i]; return stream; } static vector convert_base(const vector &a, int old_digits, int new_digits) { vector p(max(old_digits, new_digits) + 1); p[0] = 1; for (int i = 1; i < p.size(); i++) p[i] = p[i - 1] * 10; vector res; long long cur = 0; int cur_digits = 0; for (int v : a) { cur += v * p[cur_digits]; cur_digits += old_digits; while (cur_digits >= new_digits) { res.push_back(int(cur % p[new_digits])); cur /= p[new_digits]; cur_digits -= new_digits; } } res.push_back((int) cur); while (!res.empty() && res.back() == 0) res.pop_back(); return res; } bigint operator*(const bigint &v) const { if (min(z.size(), v.z.size()) < 150) return mul_simple(v); bigint res; res.sign = sign * v.sign; res.z = multiply_bigint(convert_base(z, base_digits, fft_base_digits), convert_base(v.z, base_digits, fft_base_digits), fft_base); res.z = convert_base(res.z, fft_base_digits, base_digits); res.trim(); return res; } bigint mul_simple(const bigint &v) const { bigint res; res.sign = sign * v.sign; res.z.resize(z.size() + v.z.size()); for (int i = 0; i < z.size(); ++i) if (z[i]) for (int j = 0, carry = 0; j < v.z.size() || carry; ++j) { long long cur = res.z[i + j] + (long long) z[i] * (j < v.z.size() ? v.z[j] : 0) + carry; carry = (int) (cur / base); res.z[i + j] = (int) (cur % base); } res.trim(); return res; } }; mt19937 rng(1); bigint random_bigint(int n) { string s; for (int i = 0; i < n; i++) { s += uniform_int_distribution('0', '9')(rng); } return bigint(s); } int main() { using namespace std; cin.tie(nullptr)->sync_with_stdio(false); int n; bigint m; cin >> n;n--; vector a(n); bigint mult = 1; for (auto&& e : a){ cin >> e; mult *= bigint(e); } reverse(a.begin(),a.end()); cin >> m; m += mult; reserve(n + 1); auto calc=[&](bigint m){ Fps bernoulli(n + 1); for (int i = 0; i <= n; ++i) bernoulli[i] = ifact[i + 1]; bernoulli = inv(bernoulli); auto faulhaber = [&](Poly f) -> Poly { for (int i = 0; i < sz(f); ++i) f[i] *= fact[i]; reverse(begin(f), end(f)); f *= Fps(begin(bernoulli), begin(bernoulli) + sz(f)); f.push_back(0); reverse(begin(f), end(f)); for (int i = 0; i < sz(f); ++i) f[i] *= ifact[i]; return f; }; Poly f{1}; for (int i = n; i--;) { Poly g = faulhaber(f); f = -g; Fp coeff = 1; for (int j = 1; j < sz(f); ++j) f[j] *= coeff *= a[i]; f[0] = eval(g, ((m + 1)%bigint(998244353)).longValue()); m /= bigint(a[i]); } f = faulhaber(f); return eval(f, ((m + 1)%bigint(998244353)).longValue()) - eval(f, 1); }; cout << calc(m) - calc(m-1) << '\n'; }