#line 1 "Main.cpp" #include #include #include #include #include #line 1 "nachia\\atcoder\\convolution.hpp" #line 4 "nachia\\atcoder\\convolution.hpp" #include #include #include #line 1 "nachia\\atcoder\\internal_bit.hpp" #ifdef _MSC_VER #include #endif namespace atcoder { namespace internal { // @param n `0 <= n` // @return minimum non-negative `x` s.t. `n <= 2**x` int ceil_pow2(int n) { int x = 0; while ((1U << x) < (unsigned int)(n)) x++; return x; } // @param n `1 <= n` // @return minimum non-negative `x` s.t. `(n & (1 << x)) != 0` constexpr int bsf_constexpr(unsigned int n) { int x = 0; while (!(n & (1 << x))) x++; return x; } // @param n `1 <= n` // @return minimum non-negative `x` s.t. `(n & (1 << x)) != 0` int bsf(unsigned int n) { #ifdef _MSC_VER unsigned long index; _BitScanForward(&index, n); return index; #else return __builtin_ctz(n); #endif } } // namespace internal } // namespace atcoder #line 1 "nachia\\atcoder\\static_modint.hpp" #line 1 "nachia\\atcoder\\internal_modint_base.hpp" #line 1 "nachia\\atcoder\\internal_type_traits.hpp" #line 5 "nachia\\atcoder\\internal_type_traits.hpp" #include #line 7 "nachia\\atcoder\\internal_type_traits.hpp" namespace atcoder { namespace internal { #ifndef _MSC_VER template using is_signed_int128 = typename std::conditional< std::is_same::value || std::is_same::value, std::true_type, std::false_type>::type; template using is_unsigned_int128 = typename std::conditional< std::is_same::value || std::is_same::value, std::true_type, std::false_type>::type; template using make_unsigned_int128 = typename std::conditional< std::is_same::value, __uint128_t, unsigned __int128>; template using is_integral = typename std::conditional< std::is_integral::value || is_signed_int128::value || is_unsigned_int128::value, std::true_type, std::false_type>::type; template using is_signed_int = typename std::conditional< (is_integral::value && std::is_signed::value) || is_signed_int128::value, std::true_type, std::false_type>::type; template using is_unsigned_int = typename std::conditional< (is_integral::value && std::is_unsigned::value) || is_unsigned_int128::value, std::true_type, std::false_type>::type; template using to_unsigned = typename std::conditional< is_signed_int128::value, make_unsigned_int128, typename std::conditional< std::is_signed::value, std::make_unsigned, std::common_type>::type>::type; #else template using is_integral = typename std::is_integral; template using is_signed_int = typename std::conditional< is_integral::value && std::is_signed::value, std::true_type, std::false_type>::type; template using is_unsigned_int = typename std::conditional< is_integral::value && std::is_unsigned::value, std::true_type, std::false_type>::type; template using to_unsigned = typename std::conditional< is_signed_int::value, std::make_unsigned, std::common_type>::type; #endif template using is_signed_int_t = std::enable_if_t::value>; template using is_unsigned_int_t = std::enable_if_t::value>; template using to_unsigned_t = typename to_unsigned::type; } // namespace internal } // namespace atcoder #line 7 "nachia\\atcoder\\internal_modint_base.hpp" namespace atcoder { namespace internal { struct modint_base {}; struct static_modint_base : modint_base {}; template using is_modint = std::is_base_of; template using is_modint_t = std::enable_if_t::value>; } // namespace internal } // namespace atcoder #line 1 "nachia\\atcoder\\internal_math.hpp" #line 5 "nachia\\atcoder\\internal_math.hpp" namespace atcoder { namespace internal { // @param m `1 <= m` // @return x mod m constexpr long long safe_mod(long long x, long long m){ x %= m; if(x < 0) x += m; return x; } // Fast moduler by barrett reduction // Reference: https://en.wikipedia.org/wiki/Barrett_reduction // NOTE: reconsider after Ice Lake struct barrett { using u64 = unsigned long long; unsigned int _m; u64 im; // @param m `1 <= m` barrett(unsigned int m) : _m(m), im((u64)(-1) / m + 1){} // @return m unsigned int umod() const { return _m; } // @param a `0 <= a < m` // @param b `0 <= b < m` // @return `a * b % m` unsigned int mul(unsigned int a, unsigned int b) const { u64 z = a; z *= b; #ifdef _MSC_VER u64 x; _umul128(z, im, &x); #else u64 x = (u64)(((unsigned __int128)(z)*im) >> 64); #endif unsigned int v = (unsigned int)(z - x * _m); if(_m <= v) v += _m; return v; } }; // @param n `0 <= n` // @param m `1 <= m` // @return `(x ** n) % m` constexpr long long pow_mod_constexpr(long long x, long long n, int m){ if(m == 1) return 0; unsigned int _m = (unsigned int)(m); unsigned long long r = 1, y = safe_mod(x, m); while(n){ if(n & 1) r = (r * y) % _m; y = (y * y) % _m; n >>= 1; } return r; } // Reference: // M. Forisek and J. Jancina, // Fast Primality Testing for Integers That Fit into a Machine Word // @param n `0 <= n` constexpr bool is_prime_constexpr(int n){ if(n <= 1) return false; if(n == 2 || n == 7 || n == 61) return true; if(n % 2 == 0) return false; long long d = n - 1; while(d % 2 == 0) d /= 2; for(long long a : {2, 7, 61}){ long long t = d, y = pow_mod_constexpr(a, t, n); while(t != n - 1 && y != 1 && y != n - 1){ y = y * y % n; t <<= 1; } if(y != n - 1 && t % 2 == 0) return false; } return true; } template constexpr bool is_prime = is_prime_constexpr(n); // @param b `1 <= b` // @return pair(g, x) s.t. g = gcd(a, b), xa = g (mod b), 0 <= x < b/g constexpr std::pair inv_gcd(long long a, long long b){ a = safe_mod(a, b); if(a == 0) return {b, 0}; long long s = b, t = a, m0 = 0, m1 = 1; while(t){ long long u = s / t; s -= t * u; m0 -= m1 * u; auto tmp = s; s = t; t = tmp; tmp = m0; m0 = m1; m1 = tmp; } if(m0 < 0) m0 += b / s; return {s, m0}; } // @param m must be prime constexpr int primitive_root_constexpr(int m){ if(m == 2) return 1; if(m == 167772161) return 3; if(m == 469762049) return 3; if(m == 754974721) return 11; if(m == 998244353) return 3; int divs[20] = {}; divs[0] = 2; int cnt = 1; int x = (m-1) / 2; while(x%2 == 0) x /= 2; for(int i=3; (long long)(i)*i <= x; i += 2){ if(x % i == 0){ divs[cnt++] = i; while(x % i == 0) x /= i; } } if(x>1) divs[cnt++] = x; for(int g=2; ; g++){ bool ok = true; for(int i=0; i constexpr int primitive_root = primitive_root_constexpr(m); } // namespace internal } // namespace atcoder #line 10 "nachia\\atcoder\\static_modint.hpp" namespace atcoder { template * = nullptr> struct static_modint : internal::static_modint_base { using mint = static_modint; public: static constexpr int mod(){ return m; } static mint raw(int v){ mint x; x.w = v; return x; } static_modint() : w(0){} template * = nullptr> static_modint(T v){ long long x = (long long)(v % (long long)(umod())); if(x < 0) x += umod(); w = (unsigned int)(x); } template * = nullptr> static_modint(T v){ w = (unsigned int)(v % umod()); } static_modint(bool v){ w = ((unsigned int)(v) % umod()); } unsigned int val() const { return w; } mint& operator++(){ w++; if(w == umod()) w = 0; return *this; } mint& operator--(){ if(w == 0) w = umod(); w--; return *this; } mint operator++(int){ mint result = *this; ++*this; return result; } mint operator--(int){ mint result = *this; --*this; return result; } mint& operator+=(const mint& rhs){ w += rhs.w; if(w >= umod()) w -= umod(); return *this; } mint& operator-=(const mint& rhs){ w -= rhs.w; if(w >= umod()) w += umod(); return *this; } mint& operator*=(const mint& rhs){ unsigned long long z = w; z *= rhs.w; w = (unsigned int)(z % umod()); return *this; } mint& operator/=(const mint& rhs){ return *this = *this * rhs.inv(); } mint operator+() const { return *this; } mint operator-() const { return mint() - *this; } mint pow(long long n) const { assert(0 <= n); mint x = *this, r = 1; while(n){ if(n & 1) r *= x; x *= x; n >>= 1; } return r; } mint inv() const { if(prime){ assert(w); return pow(umod() - 2); } else { auto eg = internal::inv_gcd(w, m); assert(eg.first == 1); return eg.second; } } friend mint operator+(const mint& lhs, const mint& rhs){ return mint(lhs) += rhs; } friend mint operator-(const mint& lhs, const mint& rhs){ return mint(lhs) -= rhs; } friend mint operator*(const mint& lhs, const mint& rhs){ return mint(lhs) *= rhs; } friend mint operator/(const mint& lhs, const mint& rhs){ return mint(lhs) /= rhs; } friend bool operator==(const mint& lhs, const mint& rhs){ return lhs.w == rhs.w; } friend bool operator!=(const mint& lhs, const mint& rhs){ return lhs.w != rhs.w; } private: unsigned int w; static constexpr unsigned int umod(){ return m; } static constexpr bool prime = internal::is_prime; }; using modint998244353 = static_modint<998244353>; using modint1000000007 = static_modint<1000000007>; namespace internal { template using is_static_modint = std::is_base_of; template using is_static_modint_t = std::enable_if_t::value>; } // namespace internal } // namespace atcoder #line 11 "nachia\\atcoder\\convolution.hpp" namespace atcoder { namespace internal { template , internal::is_static_modint_t* = nullptr> struct fft_info { static constexpr int rank2 = bsf_constexpr(mint::mod()-1); std::array root; std::array iroot; std::array rate2; std::array irate2; std::array rate3; std::array irate3; fft_info(){ root[rank2] = mint(g).pow((mint::mod() - 1) >> rank2); iroot[rank2] = root[rank2].inv(); for(int i=rank2-1; i>=0; i--){ root[i] = root[i+1] * root[i+1]; iroot[i] = iroot[i+1] * iroot[i+1]; } mint prod = 1, iprod = 1; for(int i=0; i<=rank2-2; i++){ rate2[i] = root[i+2] * prod; irate2[i] = iroot[i+2] * iprod; prod *= iroot[i+2]; iprod *= root[i+2]; } prod = 1; iprod = 1; for(int i=0; i<=rank2-3; i++){ rate3[i] = root[i+3] * prod; irate3[i] = iroot[i+3] * iprod; prod *= iroot[i+3]; iprod *= root[i+3]; } } }; template * = nullptr> void butterfly(std::vector& a){ int n = int(a.size()); int h = internal::ceil_pow2(n); static const fft_info info; int len = 0; while(len < h){ if(h-len == 1){ int p = 1 << (h-len-1); mint rot = 1; for(int s=0; s<(1<* = nullptr> void butterfly_inv(std::vector& a){ int n = int(a.size()); int h = internal::ceil_pow2(n); static const fft_info info; constexpr int MOD = mint::mod(); int len = h; while(len){ if(len == 1){ int p = 1 << (h-len); mint irot = 1; for(int s=0; s<(1<<(len-1)); s++){ int offset = s << (h-len+1); for(int i=0; i std::vector convolution_naive(const std::vector& a, const std::vector& b){ int n = int(a.size()), m = int(b.size()); std::vector ans(n+m-1); if(n < m){ for(int j=0; j* = nullptr> std::vector convolution_fft(std::vector a, std::vector b){ int n = int(a.size()), m = int(b.size()); int z = 1 << internal::ceil_pow2(n+m-1); a.resize(z); internal::butterfly(a); b.resize(z); internal::butterfly(b); for(int i=0; i* = nullptr> std::vector convolution(std::vector&& a, std::vector&& b){ int n = int(a.size()), m = int(b.size()); if(!n || !m) return {}; if(std::min(n, m) <= 60) return convolution_naive(a, b); return internal::convolution_fft(a, b); } template * = nullptr> std::vector convolution(const std::vector& a, const std::vector& b){ int n = int(a.size()), m = int(b.size()); if(!n || !m) return {}; if(std::min(n, m) <= 60) return convolution_naive(a, b); return internal::convolution_fft(a, b); } template ::value>* = nullptr> std::vector convolution(const std::vector& a, const std::vector& b){ int n = int(a.size()), m = int(b.size()); if(!n || !m) return {}; using mint = static_modint; std::vector a2(n), b2(m); for(int i=0; i c(n+m-1); for(int i=0; i convolution_ll( const std::vector& a, const std::vector& b) { int n = int(a.size()), m = int(b.size()); if(!n || !m) return {}; using u64 = unsigned long long; static constexpr u64 MOD1 = 754974721, MOD2 = 167772161, MOD3 = 469762049; static constexpr u64 M2M3 = MOD2 * MOD3, M1M3 = MOD1 * MOD3, M1M2 = MOD1 * MOD2; static constexpr u64 M1M2M3 = MOD1 * MOD2 * MOD3; static constexpr u64 i1 = internal::inv_gcd(M2M3, MOD1).second; static constexpr u64 i2 = internal::inv_gcd(M1M3, MOD2).second; static constexpr u64 i3 = internal::inv_gcd(M1M2, MOD3).second; auto c1 = convolution(a, b); auto c2 = convolution(a, b); auto c3 = convolution(a, b); std::vector c(n+m-1); for(int i=0; i std::vector ProductOfManyPolynomials(std::vector> poly){ if(poly.empty()) return {Modint(1)}; for(auto& p : poly) while(!p.empty() && p.back().val() == 0) p.pop_back(); for(auto& p : poly) if(p.size() == 0) return {Modint(0)}; for(size_t K=16; poly.size() != 1; K*=2){ size_t pos = poly.size(); for(size_t i=0; i K){ pos = i; continue; } poly[pos] = atcoder::convolution(poly[pos], poly[i]); std::swap(poly[i--], poly.back()); poly.pop_back(); } } return std::move(poly[0]); } } // namespace nachia #line 3 "nachia\\math\\combination.hpp" namespace nachia{ template class Comb{ private: std::vector F; std::vector iF; public: void extend(int newN){ int prevN = (int)F.size() - 1; if(prevN >= newN) return; F.resize(newN+1); iF.resize(newN+1); for(int i=prevN+1; i<=newN; i++) F[i] = F[i-1] * Modint::raw(i); iF[newN] = F[newN].inv(); for(int i=newN; i>prevN; i--) iF[i-1] = iF[i] * Modint::raw(i); } Comb(int n = 1){ F.assign(2, Modint(1)); iF.assign(2, Modint(1)); extend(n); } Modint factorial(int n) const { return F[n]; } Modint invFactorial(int n) const { return iF[n]; } Modint invOf(int n) const { return iF[n] * F[n-1]; } Modint comb(int n, int r) const { if(n < 0 || n < r || r < 0) return Modint(0); return F[n] * iF[r] * iF[n-r]; } Modint invComb(int n, int r) const { if(n < 0 || n < r || r < 0) return Modint(0); return iF[n] * F[r] * F[n-r]; } Modint perm(int n, int r) const { if(n < 0 || n < r || r < 0) return Modint(0); return F[n] * iF[n-r]; } Modint invPerm(int n, int r) const { if(n < 0 || n < r || r < 0) return Modint(0); return iF[n] * F[n-r]; } Modint operator()(int n, int r) const { return comb(n,r); } }; } // namespace nachia #line 8 "Main.cpp" using namespace std; using i32 = int32_t; using u32 = uint32_t; using i64 = int64_t; using u64 = uint64_t; #define rep(i,n) for(int i=0; i<(int)(n); i++) const i64 INF = 1001001001001001001; using Modint = atcoder::static_modint<998244353>; int main(){ const int Z = 700; nachia::Comb comb(Z); int n, m; cin >> n >> m; vector A(n), B(n); rep(i,n) cin >> A[i]; rep(i,n) cin >> B[i]; vector> Comb(m, vector(2*Z+1, 1)); auto Diff = [&](int a, int b, int d) -> Modint { return comb.comb(a+b, a+d); }; rep(i,n) rep(d,2*Z+1) Comb[i%m][d] *= Diff(A[i], B[i], d-Z); auto anss = nachia::ProductOfManyPolynomials(std::move(Comb)); auto ans = anss[Z * m]; cout << ans.val() << '\n'; return 0; } struct ios_do_not_sync{ ios_do_not_sync(){ ios::sync_with_stdio(false); cin.tie(nullptr); } } ios_do_not_sync_instance;