// #define _GLIBCXX_DEBUG #ifndef LIST__HPP_ #define LIST__HPP_ #include #include #include #include #include using uint = unsigned int; using ll = long long; using namespace std; template concept has_identity = requires (){T();}; template concept has_plus_operator = requires (T a, T b){a+b;}; template concept has_compare_operator = requires (T a, T b){a < b;}; template concept has_equal_operator = requires (T a, T b){a == b; a != b;}; template class list{ public: using value_type = T; constexpr list(): sz_(0), max_sz_(0), A(nullptr){} constexpr list(uint sz): sz_(sz), max_sz_(sz){ if (sz == 0){A = nullptr; return;} if (sz_ > 2147483647){cerr << "ValueError: too large or negative size" << endl; assert(false);} A = new T[sz_]{}; if (A == nullptr){cerr << "MomoryError: failed to allocate memory" << endl; assert(false);} } constexpr list(uint sz, T elem): sz_(sz), max_sz_(sz){ if (sz == 0){A = nullptr; return;} if (sz_ > 2147483647){cerr << "ValueError: too large or negative size" << endl; assert(false);} A = new T[sz_]{}; if (A == nullptr){cerr << "MemoryError: failed to allocate memory" << endl; assert(false);} for (int i = 0; i < sz_; i++){ A[i] = elem; } } constexpr list(initializer_list init): sz_(init.size()), max_sz_(init.size()){ A = new T[sz_]{}; if (A == nullptr){cerr << "MemoryError: failed to allocate memory" << endl; assert(false);} for (int i = 0; i < sz_; i++){ A[i] = *(init.begin()+i); } }; // constexpr list(const range& r){ // if (r.step_ == 0){cerr << "ValueError: step argument must not be zero" << endl; assert(false);} // if (r.step_ > 0){ // int imax = r.begin_ >= r.end_ ? 0 : (r.end_-r.begin_+r.step_-1)/r.step_; // sz_ = imax; // max_sz_ = imax; // A = new T[sz_]{}; // if (A == nullptr){cerr << "MemoryError: failed to allocate memory" << endl; assert(false);} // for (int i = 0; i < imax; i++){ // int target = r.begin_+r.step_*i; // A[i] = target; // } // } // else{ // int imax = r.end_ >= r.begin_ ? 0 : (r.begin_-r.end_-r.step_-1)/(-r.step_); // sz_ = imax; // max_sz_ = imax; // A = new T[sz_]{}; // if (A == nullptr){cerr << "MemoryError: failed to allocate memory" << endl; assert(false);} // for (int i = 0; i < imax; i++){ // int target = r.begin_+r.step_*i; // A[i] = target; // } // } // } constexpr list(const list& other): sz_(other.sz_), max_sz_(other.max_sz_){ A = new T[max_sz_]{}; for (int i = 0; i < sz_; i++){ A[i] = other.A[i]; } } constexpr list(list&& other) noexcept : sz_(other.sz_), max_sz_(other.max_sz_){ A = other.A; other.sz_ = 0; other.max_sz_ = 0; other.A = nullptr; } constexpr ~list() { delete[] A; } class iterator{ public: using difference_type = ll; using value_type = T; using pointer = T*; using reference = T&; using iterator_category = std::random_access_iterator_tag; iterator() = delete; constexpr iterator(const list* listptr_init, const int &idx_init): listptr(listptr_init), idx(idx_init){} inline constexpr value_type& operator* () const {return listptr->A[this->check_idx()];} inline constexpr value_type* operator->() const {return &listptr->A[this->check_idx()];} inline constexpr bool operator==(const iterator &other) const {return idx == other.idx;} inline constexpr bool operator!=(const iterator &other) const {return idx != other.idx;} inline constexpr bool operator<(const iterator &other) const {return idx < other.idx;} inline constexpr bool operator>(const iterator &other) const {return idx > other.idx;} inline constexpr bool operator<=(const iterator &other) const {return idx <= other.idx;} inline constexpr bool operator>=(const iterator &other) const {return idx >= other.idx;} inline constexpr iterator operator++(){ this->idx++; return *this; } inline constexpr iterator operator++(int){ iterator temp = *this; this->idx++; return temp; } inline constexpr iterator operator--(){ this->idx--; return *this; } inline constexpr iterator operator--(int){ iterator temp = *this; this->idx--; return temp; } inline constexpr iterator& operator+=(int amount){ this->idx += amount; return *this; } inline constexpr iterator operator+(int amount) const { iterator temp = *this; temp.idx += amount; return temp; } inline constexpr iterator& operator-=(const int &amount){ this->idx -= amount; return *this; } inline constexpr iterator operator-(const int &amount) const{ iterator temp = *this; temp.idx -= amount; return temp; } inline constexpr difference_type operator-(const iterator &other) const{ return idx - other.idx; } inline constexpr int check_idx() const { if (idx < 0 || idx >= listptr->sz_){ cerr << "IteratorIndexError: index out of range. current size: " << listptr->sz_ << " target index: " << idx << endl; assert(false); } return idx; } private: const list* listptr; int idx; }; inline constexpr iterator begin(){return iterator(this, 0);} inline constexpr iterator begin() const {return iterator(this, 0);} inline constexpr iterator end(){return iterator(this, sz_);} inline constexpr iterator end() const {return iterator(this, sz_);} inline constexpr list& operator=(const list& other){ if (this == &other){return *this;} sz_ = other.sz_; max_sz_ = other.max_sz_; delete[] A; A = new T[max_sz_]{}; for (int i = 0; i < sz_; i++){ A[i] = other.A[i]; } return *this; } inline constexpr list& operator=(list&& other) noexcept { if (this == &other){return *this;} delete[] A; sz_ = other.sz_; max_sz_ = other.max_sz_; A = other.A; other.sz_ = 0; other.max_sz_ = 0; other.A = nullptr; return *this; } inline constexpr bool operator==(const list& other) const { if (sz_ != other.sz_){return false;} bool res = true; for (int i = 0; i < sz_; i++){ if (A[i] != other.A[i]){ res = false; break; } } return res; } inline constexpr bool operator!=(const list& other) const { return !this->operator==(other); } inline constexpr bool operator<(const list& other) const { int lcp_back = 0; for (int i = 0, sz2 = min(sz_, other.sz_); i < sz2; i++){ if (A[i] != other.A[i]){break;} lcp_back++; } if (lcp_back == sz_ && lcp_back == other.sz_){return false;} else if (lcp_back == sz_){return true;} else if (lcp_back == other.sz_){return false;} else{return A[lcp_back] < other.A[lcp_back];} } inline constexpr bool operator<=(const list& other) const { int lcp_back = 0; for (int i = 0, sz2 = min(sz_, other.sz_); i < sz2; i++){ if (A[i] != other.A[i]){break;} lcp_back++; } if (lcp_back == sz_ && lcp_back == other.sz_){return true;} else if (lcp_back == sz_){return true;} else if (lcp_back == other.sz_){return false;} else{return A[lcp_back] < other.A[lcp_back];} } inline constexpr bool operator>(const list& other) const {return other < *this;} inline constexpr bool operator>=(const list& other) const {return other <= *this;} inline constexpr list& operator+=(const list& other){ for (int i = 0, sz2 = other.size(); i < sz2; i++){ append(other.A[i]); } return *this; } inline constexpr list operator+(const list& other) const { return list(*this) += other; } inline constexpr list& operator+=(const T& val){ append(val); return *this; } inline constexpr list operator+(const T& val) const { return list(*this) += val; } inline constexpr void append(const T& val){ if (sz_ < max_sz_){ A[sz_] = val; sz_++; } else{ if (max_sz_ < 2){max_sz_ = 2;} else{max_sz_ += max_sz_>>1;} auto B = new T[max_sz_]{}; B[sz_] = val; for (int i = 0; i < sz_; i++){B[i] = std::move(A[i]);} sz_++; delete[] A; A = B; } } inline constexpr void append(T&& val){ if (sz_ < max_sz_){ A[sz_] = std::move(val); sz_++; } else{ if (max_sz_ < 2){max_sz_ = 2;} else{max_sz_ += max_sz_>>1;} auto B = new T[max_sz_]{}; B[sz_] = std::move(val); for (int i = 0; i < sz_; i++){B[i] = std::move(A[i]);} sz_++; delete[] A; A = B; } } inline constexpr T pop(){ if (sz_ == 0){cerr << "IndexError: pop from empty list" << endl; assert(false);} sz_--; return std::move(A[sz_]); } inline constexpr void reserve(uint new_sz){ if (sz_ > 2147483647){cerr << "ValueError: too large or negative size" << endl; assert(false);} if ((int)new_sz <= max_sz_){return;} max_sz_ = new_sz; auto B = new T[new_sz]{}; if (B == nullptr){cerr << "MemoryError: failed to reallocate memory" << endl; assert(false);} for (int i = 0; i < sz_; i++){B[i] = std::move(A[i]);} delete[] A; A = B; } inline constexpr void resize(uint new_sz, T elem = T()){ if (sz_ > 2147483647){cerr << "ValueError: too large or negative size" << endl; assert(false);} if ((int)new_sz <= max_sz_){ while (sz_ > (int)new_sz){pop();} for (int i = sz_, new_sz_int = new_sz; i < new_sz_int; i++){A[i] = elem;} sz_ = new_sz; return; } max_sz_ = new_sz + (new_sz >> 1); auto B = new T[max_sz_]{}; if (B == nullptr){cerr << "MemoryError: failed to reallocate memory" << endl; assert(false);} for (int i = 0; i < sz_; i++){B[i] = std::move(A[i]);} for (int i = sz_, new_sz_int = new_sz; i < new_sz_int; i++){B[i] = elem;} sz_ = new_sz; delete[] A; A = B; } template inline constexpr void assign(InputIterator first, InputIterator last){ reserve(last-first); while (first != last){append(*first); first++;} } inline constexpr void assign(uint n, const T& val){ reserve(n); sz_ = n; for (uint i = 0; i < n; i++){ A[i] = val; } } inline constexpr iterator insert(iterator pos, const T& val){ int idx = pos.check_idx(); append(val); for (int i = idx; i < sz_-1; i++){swap(A[i], A[sz_-1]);} return pos; } // inline constexpr void insert(iterator pos, iterator begin, iterator end) inline constexpr iterator erase(iterator pos){ int idx = pos.check_idx(); for (int i = idx; i < sz_-1; i++){ swap(A[i], A[i+1]); } sz_ -= 1; return pos; } inline constexpr iterator erase(iterator start, iterator end){ int startidx = start.check_idx(), endidx = end.check_idx(); if (startidx > endidx){cerr << "IndexError: invalid interval: [start, end) = []" << startidx << ", " << endidx << ")" << endl; assert(false);} for (int i = startidx; i < endidx; i++){ A[i].~T(); } if (startidx < endidx){ for (int i = endidx; i < sz_; i++){ swap(A[i+startidx-endidx], A[i]); } } sz_ -= endidx-startidx; return end; } inline constexpr void clear(){sz_ = 0; max_sz_ = 0; delete[] A;} inline constexpr const T& operator[](int idx) const { check_and_adjust_idx(idx); return A[idx]; } inline constexpr T& operator[](int idx){ check_and_adjust_idx(idx); return A[idx]; } inline constexpr T& front(){return (*this)[0];} inline constexpr const T& front() const {return (*this)[0];} inline constexpr T& back(){return (*this)[-1];} inline constexpr const T& back() const {return (*this)[-1];} inline constexpr list operator[](int left, int right, int step = 1) const { if (step == 0){cerr << "ValueError: step argument must not be zero" << endl; assert(false);} if (step > 0){ int imax = left >= right ? 0 : (right-left+step-1)/step; list B(imax); for (int i = 0; i < imax; i++){ int target = left+step*i; if (target < 0 || target >= sz_){cerr << "IndexError: slice index out of range. current size: " << sz_ << " target index: " << target << endl; assert(false);} B[i] = A[target]; } return std::move(B); } else{ int imax = right >= left ? 0 : (left-right-step-1)/(-step); list B(imax); for (int i = 0; i < imax; i++){ int target = left+step*i; if (target < 0 || target >= sz_){cerr << "IndexError: slice index out of range. current size: " << sz_ << " target index: " << target << endl; assert(false);} B[i] = A[target]; } return std::move(B); } } inline constexpr T* data() {return empty() ? nullptr : &A[0];} inline constexpr const T* data() const {return empty() ? nullptr : &A[0];} inline constexpr T sum() const requires has_plus_operator && has_identity{ auto res = T(); for (int i = 0; i < sz_; i++){ res += A[i]; } return res; } template inline constexpr U sum(U init) const requires has_plus_operator{ for (int i = 0; i < sz_; i++){ init += A[i]; } return init; } inline constexpr void sort(bool reverse = false) requires has_compare_operator { if (reverse){ std::sort(begin(), end(), std::move([](const T& a, const T& b){return b < a;})); } else{ std::sort(begin(), end()); } } inline constexpr void sort(const function& f) requires has_compare_operator { std::sort(begin(), end(), f); } inline constexpr void reverse(){ std::reverse(begin(), end()); } inline constexpr ll count(const T& val){ ll res = 0; for (int i = 0; i < sz_; i++){res += A[i] == val;} return res; } inline constexpr void apply(const function& f){for (int i = 0; i < sz_; i++){A[i] = f(A[i]);}} inline constexpr list> RLE(){ list> res; for (int l = 0, r = 0; l < sz_;){ while (r < sz_ && A[l] == A[r]){ r++; } res.append(make_pair(r-l, A[l])); l = r; } return res; } inline constexpr iterator lower_bound(const T& val) const requires has_compare_operator {return std::lower_bound(begin(), end(), val);} inline constexpr iterator upper_bound(const T& val) const requires has_compare_operator {return std::upper_bound(begin(), end(), val);} inline constexpr ll size() const {return sz_;} inline constexpr ll maxsize() const {return max_sz_;} inline constexpr bool empty() const {return sz_ == 0;} private: inline constexpr void check_and_adjust_idx(int& idx) const { if (idx >= sz_){cerr << "IndexError: index out of range. current size: " << sz_ << " target index: " << idx << endl; assert(false);} if (idx < 0){ if (idx < -sz_){cerr << "IndexError: index out of range. current size: " << sz_ << " target index: " << idx << endl; assert(false);} idx += sz_; } } int sz_; int max_sz_; T* A; }; #endif /* LIST__HPP_ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // #include using namespace std; using ll = long long; using lll = __int128_t; using ull = unsigned long long; #ifdef BOOST_VERSION using bll = boost::multiprecision::cpp_int; #endif using ld = long double; using pii = array; using pll = array; using plll = array; using pld = array; using tll = array; #define vall(A) A.begin(), A.end() template inline bool btest(T K, int i){return K&(1ull< inline constexpr ll len(const T& A){return ssize(A);} #endif constexpr ll pow2ll[63] = {1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288,1048576,2097152,4194304,8388608,16777216,33554432,67108864,134217728,268435456,536870912,1073741824,2147483648,4294967296,8589934592,17179869184,34359738368,68719476736,137438953472,274877906944,549755813888,1099511627776,2199023255552,4398046511104,8796093022208,17592186044416,35184372088832,70368744177664,140737488355328,281474976710656,562949953421312,1125899906842624,2251799813685248,4503599627370496,9007199254740992,18014398509481984,36028797018963968,72057594037927936,144115188075855872,288230376151711744,576460752303423488,1152921504606846976,2305843009213693952,4611686018427387904}; constexpr ll pow10ll[19] = {1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000,10000000000,100000000000,1000000000000,10000000000000,100000000000000,1000000000000000,10000000000000000,100000000000000000,1000000000000000000}; constexpr ll di[4] = {0,1,0,-1}; constexpr ll di8[8] = {0,1,1,1,0,-1,-1,-1}; constexpr ll dj[4] = {1,0,-1,0}; constexpr ll dj8[8] = {1,1,0,-1,-1,-1,0,1}; #ifndef FASTIO__HPP_ #define FASTIO__HPP_ #include #include #include #include #include #include #include #include #include #include #include #include #include // C++20. Integer input must fit the destination type. For modint input, the // magnitude must fit uint64_t; negative values are reduced modulo the modulus. // FILE* is borrowed: it must outlive the reader/writer. Do not mix buffered // readers on one stream. The default input buffer is intended for batch judges; // interactive programs need BasicInput<1> and explicit output flush(). namespace fastio { namespace detail { template inline constexpr bool integer = std::is_integral_v; template struct unsigned_type { using type = std::make_unsigned_t; }; template<> struct unsigned_type { using type = unsigned char; }; #if defined(__SIZEOF_INT128__) template<> inline constexpr bool integer<__int128_t> = true; template<> inline constexpr bool integer<__uint128_t> = true; template<> struct unsigned_type<__int128_t> { using type = __uint128_t; }; template<> struct unsigned_type<__uint128_t> { using type = __uint128_t; }; #endif template using unsigned_t = typename unsigned_type::type; template inline constexpr bool signed_integer = integer && T(-1) < T(0); template concept modular = requires(T x) { x.val; } && (requires { T::mod_value; } || requires { T::mod(); }); template constexpr auto modulus() { if constexpr (requires { T::mod_value; }) return T::mod_value; else return T::mod(); } template concept indexed_range = requires(T& x) { std::size(x); x[0]; }; template concept tuple_like = requires { std::tuple_size::value; }; template inline constexpr bool unsupported = false; inline constexpr auto digit_pairs = [] { std::array result{}; for (int i = 0; i < 100; ++i) { result[2*i] = char('0'+i/10); result[2*i+1] = char('0'+i%10); } return result; }(); inline constexpr auto digit_quads = [] { std::array,10000> result{}; for (unsigned i = 0; i < 10000; ++i) { result[i] = {char('0'+i/1000),char('0'+i/100%10), char('0'+i/10%10),char('0'+i%10)}; } return result; }(); inline char* leading_quad(char* p, unsigned value) { if (value >= 1000) { std::memcpy(p,digit_quads[value].data(),4); return p+4; } if (value >= 100) { *p++ = char('0'+value/100); value %= 100; } else if (value < 10) { *p++ = char('0'+value); return p; } std::memcpy(p,digit_pairs.data()+2*value,2); return p+2; } inline char* decimal32(char* p, std::uint32_t value) { if (value >= 100000000) { p = leading_quad(p,value/100000000); value %= 100000000; std::memcpy(p,digit_quads[value/10000].data(),4); std::memcpy(p+4,digit_quads[value%10000].data(),4); return p+8; } if (value >= 10000) { p = leading_quad(p,value/10000); std::memcpy(p,digit_quads[value%10000].data(),4); return p+4; } return leading_quad(p,value); } template inline constexpr bool output32 = [] { if constexpr (integer) return sizeof(T) <= 4 && !std::is_same_v; else if constexpr (modular) return integer && sizeof(T{}.val) <= 4; else return false; }(); template inline char* decimal_small(char* p, const T& value) { if constexpr (modular) return decimal_small(p,value.val); else { std::uint32_t u = static_cast(value); if constexpr (signed_integer) { if (value < 0) { *p++ = '-'; u = 0-u; } } return decimal32(p,u); } } } // namespace detail template class BasicInput { static_assert(BufferSize > 0); std::FILE* file_; char buffer_[BufferSize+1]; std::size_t position_ = 0, size_ = 0; bool good_ = true; int peek() { if (position_ == size_) { size_ = std::fread(buffer_, 1, BufferSize, file_); buffer_[size_] = '\0'; position_ = 0; if (size_ == 0) return EOF; } return static_cast(buffer_[position_]); } int skip_space() { int c; while ((c = peek()) != EOF && c <= ' ') ++position_; return c; } template bool magnitude(U& value, bool& negative) { int c = skip_space(); if (c == EOF) return false; negative = c == '-'; if (c == '-' || c == '+') { ++position_; c = peek(); } if (static_cast(c-'0') > 9) return false; value = 0; do { const char* p = buffer_+position_; if constexpr (sizeof(U) >= 4 && std::endian::native == std::endian::little) { while (buffer_+size_-p >= 8) { std::uint64_t block; std::memcpy(&block,p,8); // All eight bytes must be ASCII decimal digits. if (((block+0x4646464646464646ULL) | (block-0x3030303030303030ULL)) & 0x8080808080808080ULL) break; block -= 0x3030303030303030ULL; block = (block*10+(block>>8)) & 0x00ff00ff00ff00ffULL; block = (block*100+(block>>16)) & 0x0000ffff0000ffffULL; block = (block*10000+(block>>32)) & 0xffffffffULL; value = value*100000000+static_cast(block); p += 8; if constexpr (SingleBlock || sizeof(U) == 4) break; } } unsigned digit; // The sentinel stops this loop without an EOF/buffer check for // every digit. Only a token crossing a refill needs the outer loop. while ((digit = static_cast(static_cast(*p)-'0')) <= 9) { value = value*10+digit; ++p; } position_ = static_cast(p-buffer_); if (position_ != size_) break; c = peek(); } while (static_cast(c-'0') <= 9); return true; } template bool read_one(T& value) { if constexpr (std::is_same_v) { const int c = skip_space(); if (c == EOF) return false; value = static_cast(c); ++position_; return true; } else if constexpr (detail::integer) { detail::unsigned_t u; bool negative; if (!magnitude(u,negative)) return false; if constexpr (detail::signed_integer) { // Never negate the minimum signed value, even for int128. value = negative && u != 0 ? -static_cast(u-1)-1 : static_cast(u); } else { if (negative || (std::is_same_v && u > 1)) return false; value = static_cast(u); } return true; } else if constexpr (detail::modular) { std::uint64_t u; bool negative; // Most modular coefficients have at most ten digits. After the // first eight, parse the short tail without another wide probe. if (!magnitude(u,negative)) return false; const auto mod = detail::modulus(); if (u >= mod) u %= mod; value.val = negative && u != 0 ? mod-u : u; return true; } else if constexpr (std::is_same_v) { int c = skip_space(); if (c == EOF) return false; value.clear(); do { value.push_back(static_cast(c)); ++position_; c = peek(); } while (c != EOF && c > ' '); return true; } else if constexpr (std::is_floating_point_v) { std::string token; if (!read_one(token)) return false; const char* first = token.data(); const char* last = first+token.size(); if (first != last && *first == '+') ++first; const auto result = std::from_chars(first,last,value); return result.ec == std::errc{} && result.ptr == last; } else if constexpr (detail::indexed_range) { const auto n = std::size(value); if constexpr (requires { std::data(value); }) { auto* data = std::data(value); for (decltype(std::size(value)) i = 0; i < n; ++i) if (!read_one(data[i])) return false; } else { for (decltype(std::size(value)) i = 0; i < n; ++i) if (!read_one(value[i])) return false; } return true; } else if constexpr (detail::tuple_like) { return std::apply([&](auto&... x) { return (read_one(x) && ...); },value); } else { static_assert(detail::unsupported, "Unsupported fastio input type"); } } public: explicit BasicInput(std::FILE* file = stdin) : file_(file) {} BasicInput(const BasicInput&) = delete; BasicInput& operator=(const BasicInput&) = delete; template bool read(T&... values) { if (!good_) return false; return good_ = (read_one(values) && ...); } template BasicInput& operator>>(T& value) { read(value); return *this; } explicit operator bool() const { return good_; } }; template class BasicOutput { static_assert(BufferSize > 0); std::FILE* file_; char buffer_[BufferSize]; std::size_t size_ = 0; bool good_ = true; bool drain() { if (!good_) return false; std::size_t done = 0; while (done < size_) { const auto n = std::fwrite(buffer_+done,1,size_-done,file_); if (n == 0) { size_ = 0; return good_ = false; } done += n; } size_ = 0; return true; } bool put(char c) { if (!good_ || (size_ == BufferSize && !drain())) return false; buffer_[size_++] = c; return true; } bool bytes(const char* data, std::size_t count) { if (!good_) return false; while (count != 0) { if (size_ == BufferSize && !drain()) return false; const auto room = BufferSize-size_; const auto n = count < room ? count : room; std::memcpy(buffer_+size_,data,n); size_ += n; data += n; count -= n; } return true; } template bool unsigned_number(U value, bool negative) { if constexpr (sizeof(U) <= 4 && BufferSize >= 12) { if (!good_ || (BufferSize-size_ < 12 && !drain())) return false; char* p = buffer_+size_; if (negative) *p++ = '-'; p = detail::decimal32(p,value); size_ = static_cast(p-buffer_); return true; } // 128-bit unsigned integers need at most 39 decimal digits. char digits[3*sizeof(U)+2]; char* const end = digits+sizeof(digits); char* p = end; while (value >= 100) { const U q = value/100; const unsigned r = static_cast(value-q*100); p -= 2; std::memcpy(p,detail::digit_pairs.data()+2*r,2); value = q; } if (value < 10) *--p = char('0'+static_cast(value)); else { p -= 2; std::memcpy(p,detail::digit_pairs.data()+2*static_cast(value),2); } if (negative) *--p = '-'; if constexpr (BufferSize >= sizeof(digits)) { if (!good_ || (BufferSize-size_ < sizeof(digits) && !drain())) return false; auto count = static_cast(end-p); char* dest = buffer_+size_; size_ += count; // Constant-size copies avoid a library call per integer. Each copy // stays inside the initialized digits and the reserved buffer area. if constexpr (sizeof(digits) >= 8) while (count >= 8) { std::memcpy(dest,p,8); dest += 8; p += 8; count -= 8; } if (count >= 4) { std::memcpy(dest,p,4); dest += 4; p += 4; count -= 4; } if (count >= 2) { std::memcpy(dest,p,2); dest += 2; p += 2; count -= 2; } if (count) *dest = *p; return true; } return bytes(p,static_cast(end-p)); } template bool write_one(const T& value) { if constexpr (std::is_same_v) { return put(value); } else if constexpr (detail::integer) { using U = detail::unsigned_t; U u = static_cast(value); bool negative = false; if constexpr (detail::signed_integer) { negative = value < 0; if (negative) u = U(0)-u; } return unsigned_number(u,negative); } else if constexpr (detail::modular) { return write_one(value.val); } else if constexpr (std::is_convertible_v) { const std::string_view text(value); return bytes(text.data(),text.size()); } else if constexpr (std::is_floating_point_v) { char text[128]; const auto result = std::to_chars(text,text+sizeof(text),value); if (result.ec != std::errc{}) return false; return bytes(text,static_cast(result.ptr-text)); } else if constexpr (detail::indexed_range) { const auto n = std::size(value); using Element = std::remove_cvref_t; if constexpr (detail::indexed_range || detail::tuple_like){ for (decltype(std::size(value)) i = 0; i < n; ++i) { if (i != 0 && !put('\n')) return false; if (!write_one(value[i])) return false; } return true; } if constexpr (BufferSize >= 32 && detail::output32 && requires { std::data(value); }) { const auto* data = std::data(value); char* p = buffer_+size_; for (decltype(std::size(value)) i = 0; i < n; ++i) { if (buffer_+BufferSize-p < 12) { size_ = static_cast(p-buffer_); if (!drain()) return false; p = buffer_; } if (i != 0) *p++ = ' '; p = detail::decimal_small(p,data[i]); } size_ = static_cast(p-buffer_); return true; } for (decltype(std::size(value)) i = 0; i < n; ++i) { if (i != 0 && !put(' ')) return false; if (!write_one(value[i])) return false; } return true; } else if constexpr (detail::tuple_like) { [[maybe_unused]] bool first = true; return std::apply([&](const auto&... x) { return (((std::exchange(first,false) || put(' ')) && write_one(x)) && ...); },value); } else { static_assert(detail::unsupported, "Unsupported fastio output type"); } } public: explicit BasicOutput(std::FILE* file = stdout) : file_(file) {} BasicOutput(const BasicOutput&) = delete; BasicOutput& operator=(const BasicOutput&) = delete; ~BasicOutput() { flush(); } // write concatenates its arguments; print separates them with spaces and // appends a newline. Ranges and tuples use spaces between their elements. template bool write(const T&... values) { if (!good_) return false; return good_ = (write_one(values) && ...); } template bool print(const T&... values) { if (!good_) return false; [[maybe_unused]] bool first = true; good_ = (((std::exchange(first,false) || put(' ')) && write_one(values)) && ...); return good_ && put('\n'); } template bool printflush(const T&... values) { if (!good_) return false; [[maybe_unused]] bool first = true; good_ = (((std::exchange(first,false) || put(' ')) && write_one(values)) && ...); return good_ && put('\n') && flush(); } bool flush() { return drain() && (good_ = std::fflush(file_) == 0); } template BasicOutput& operator<<(const T& value) { write(value); return *this; } explicit operator bool() const { return good_; } }; #ifdef LOCAL_a1073741824 using FastInput = BasicInput<1>; #else using FastInput = BasicInput<>; #endif using FastOutput = BasicOutput<>; // template bool read(T&... values) { return in.read(values...); } // template bool write(const T&... values) { return out.write(values...); } // template bool print(const T&... values) { return out.print(values...); } // template bool printflush(const T&... values) { return out.print(values...) && out.flush(); } // inline bool flush() { return out.flush(); } } // namespace fastio #endif /* FASTIO__HPP_ */ #ifndef FPS_OPERATION_HPP_ #define FPS_OPERATION_HPP_ #include #include #include #if defined(__x86_64__) || defined(__i386__) #include #endif #ifndef DEQUE__HPP_ #define DEQUE__HPP_ #include #include #include using namespace std; using ll = long long; using uint = unsigned int; template class delist{ public: inline constexpr delist(){A = deque();} inline constexpr delist(uint sz, const T& val = T()){A = deque(sz, val);} template inline constexpr delist(InputIterator first, InputIterator last){A = deque(first, last);} inline constexpr void append(const T& val){A.push_back(val);} inline constexpr void appendleft(const T& val){A.push_front(val);} inline constexpr T pop(){ if (A.empty()){cerr << "IndexError: pop from empty list" << endl;} auto temp = std::move(A.back()); A.pop_back(); return std::move(temp); } inline constexpr T popleft(){ if (A.empty()){cerr << "IndexError: pop from empty list" << endl;} auto temp = std::move(A.front()); A.pop_front(); return std::move(temp); } inline constexpr T& front(){return A.front();} inline constexpr const T& front() const {return A.front();} inline constexpr T& back(){return A.back();} inline constexpr const T& back() const {return A.back();} inline constexpr _Deque_iterator begin(){return A.begin();} inline constexpr _Deque_iterator begin() const {return A.begin();} inline constexpr _Deque_iterator end(){return A.end();} inline constexpr _Deque_iterator end() const {return A.end();} inline constexpr ll size(){return A.size();} inline constexpr bool empty(){return A.empty();} private: deque A; }; #endif /* DEQUE__HPP_ */ #ifndef FPS__HPP_ #define FPS__HPP_ #include #include #include #ifndef MODINT__HPP_ #define MODINT__HPP_ #include #ifndef MATH_FUNCTION_HPP_ #define MATH_FUNCTION_HPP_ #include #include #include using namespace std; using ll = long long; using ld = long double; using ull = unsigned long long; using ulll = __uint128_t; #ifdef BOOST_VERSION using bll = boost::multiprecision::cpp_int; #endif constexpr ll getdigit(ull x){ if (x < 1000000000){ if (x < 100000){ return x == 0 ? 0 : x < 10 ? 1 : x < 100 ? 2 : x < 1000 ? 3 : x < 10000 ? 4 : 5; } else{ return x < 1000000 ? 6 : x < 10000000 ? 7 : x < 100000000 ? 8 : 9; } } else{ if (x < 100000000000000){ return x < 10000000000 ? 10 : x < 100000000000 ? 11 : x < 1000000000000 ? 12 : x < 10000000000000 ? 13 : 14; } else{ return x < 1000000000000000 ? 15 : x < 10000000000000000 ? 16 : x < 100000000000000000 ? 17 : x < 1000000000000000000 ? 18 : x < 10000000000000000000u ? 19 : 20; } } } /// @brief a^bをmで割った余りを返す。bに関して対数時間で計算できる constexpr ll modpow(ll a, ull b, const ll m){ ll t = a%m; ll ans = (m == 1 ? 0 : 1); while (b > 0){ if (b&1){ ans = (ans*t)%m; } b >>= 1; t = (t*t)%m; } return ans; } /// @brief a^bをmで割った余りを返す。bに関して対数時間で計算できる。mはコンパイル時に決定している必要がある template constexpr ll modpow(ll a, ull b){ ll t = a%m; ll ans = (m == 1 ? 0 : 1); while (b > 0){ if (b&1){ ans = (ans*t)%m; } b >>= 1; t = (t*t)%m; } return ans; } /// @brief a^nを返す。bに関して対数時間で計算できる。 template constexpr T powll(T a, T n){ T t = a; T ans = 1; while (n > 0){ if (n%2){ ans *= t; } n >>= 1; t *= t; } return ans; } /// @brief a^nを返す。bに関して対数時間で計算できる template<> constexpr ll powll(ll a, ll n){ ll t = a; ll ans = 1; while (n > 0){ if (n&1){ ans *= t; } n >>= 1; t *= t; } return ans; } /// @brief floor(sqrt(N))を返す。1.5×10^19まで対応 constexpr ll isqrt(ull N){ assert(N <= 15000000000000000000ull); ull ret = sqrt(N); while (ret*ret > N){ ret--; } while ((ret+1)*(ret+1) <= N){ ret++; } return ret; } #ifdef BOOST_VERSION /// @brief floor(sqrt(N))を返す。多倍長整数に対応 constexpr bll isqrt_large(bll N){ bll ret = sqrt(N); while (ret*ret > N){ ret--; } while ((ret+1)*(ret+1) <= N){ ret++; } return ret; } #endif /// @brief floor(log_a(L))を返す constexpr ll ilog(ll a, ll L){ __int128_t t = 1; ll ans = 0; while (t <= L){ ans++; t *= a; } return ans-1; } /// @brief 有理数のfloorを求める。 floor(y/x) template constexpr inline T floor2(T y, T x){ if ((x^y) > 0){ x = x > 0 ? x : -x; y = y > 0 ? y : -y; return y/x; } else if ((x^y) < 0){ x = x > 0 ? x : -x; y = y > 0 ? y : -y; return -((y+x-1)/x); } else{ return y/x; } } /// @brief 有理数のceilを求める。 ceil(y/x) template constexpr inline T ceil2(T y, T x){ if ((x^y) > 0){ x = x > 0 ? x : -x; y = y > 0 ? y : -y; return (y+x-1)/x; } else if ((x^y) < 0){ x = x > 0 ? x : -x; y = y > 0 ? y : -y; return -(y/x); } else{ return y/x; } } /// @brief 一次不定方程式ax+by=gcd(a,b)の解を1つ見つける /// @param a `a>=0`である必要がある /// @param b `b>=0`である必要がある /// @return {x,y,gcd(a,b)} template constexpr array axby1(T a, T b){ T x = 1, y = 0; T z = 0, w = 1; T tmp = 0; while (b){ T p = a/b, q = a%b; tmp = x - y * p; x = y; y = tmp; tmp = z - w * p; z = w; w = tmp; a = b; b = q; } return {x, z, a}; } /// @brief 1/a mod Mを求める template constexpr T inverse_mod(T a, U M){ auto temp = axby1(a,(T)M); assert(temp[2] == 1); return (M+temp[0])%M; } /// @brief sqrt(a) mod Mを求める。ないなら-1が返される。 template inline constexpr ll cipolla(ll a){ a %= M; if (M == 2) return a; if (a == 0) return 0; ll z = (M-1)/2; if (modpow(a, z) != 1){return -1;} int b = 0; while (modpow((b*b+M-a)%M, z) == 1){ b++; } array x{1,0}; array y{b, 1}; ll w = (b*b+M-a)%M; z++; while (z){ if (z&1){ ll temp = x[0]; x[0] = x[0]*y[0]%M+x[1]*y[1]%M*w%M; if (x[0] >= M){x[0] -= M;} x[1] = temp*y[1]%M+x[1]*y[0]%M; if (x[1] >= M){x[1] -= M;} } ll temp = y[0]; y[0] = y[0]*y[0]%M+y[1]*y[1]%M*w%M; if (y[0] >= M){y[0] -= M;} y[1] = 2*temp*y[1]%M; z >>= 1; } return x[0]; } inline constexpr ll cipolla(ll a, const ll M){ a %= M; if (M == 2) return a; if (a == 0) return 0; ll z = (M-1)/2; if (modpow(a, z, M) != 1){return -1;} int b = 0; while (modpow((b*b+M-a)%M, z, M) == 1){ b++; } array x{1,0}; array y{b, 1}; ll w = (b*b+M-a)%M; z++; while (z){ if (z&1){ ll temp = x[0]; x[0] = x[0]*y[0]%M+x[1]*y[1]%M*w%M; if (x[0] >= M){x[0] -= M;} x[1] = temp*y[1]%M+x[1]*y[0]%M; if (x[1] >= M){x[1] -= M;} } ll temp = y[0]; y[0] = y[0]*y[0]%M+y[1]*y[1]%M*w%M; if (y[0] >= M){y[0] -= M;} y[1] = 2*temp*y[1]%M; z >>= 1; } return x[0]; } /// @brief x以下の最大の2冪を返す。0は0が返る。 inline constexpr ull lowerpow2(ull x){ if (x == 0){return 0;} return 1ull<<(63-__builtin_clzll(x)); } /// @brief x以上の最小の2冪を返す。0は0が返る。 inline constexpr ull upperpow2(ull x){ if (x == 0){return 0;} if (x == 1){return 1;} return 1ull<<(64-__builtin_clzll(x-1)); } /// @brief xのpopcountを求める inline constexpr int popcount(ull x){ return __builtin_popcountll(x); } /// @brief xのbit lengthを求める。 inline constexpr int bit_length(ull x){ if (x == 0){return 0;} return 64-__builtin_clzll(x); } #endif /* MATH_FUNCTION_HPP_ */ using ll = long long; using uint = unsigned int; using ull = unsigned long long; using namespace std; template struct constant_modint { static constexpr uint mod_value = M; uint val; constant_modint() : val(0) {} template constant_modint(T x) { ll y = (ll)(x % (ll)M); if (y < 0) y += M; val = y; } constant_modint& operator+=(const constant_modint& rhs) { val += rhs.val; if (val >= M) val -= M; return *this; } constant_modint& operator-=(const constant_modint& rhs) { if (val < rhs.val) val += M; val -= rhs.val; return *this; } constant_modint& operator*=(const constant_modint& rhs) { val = (ull)val * rhs.val % M; return *this; } constant_modint& operator/=(const constant_modint& rhs) { return *this *= rhs.inv(); } constant_modint operator+() const { return *this; } constant_modint operator-() const { return constant_modint(0) - *this; } friend constant_modint operator+(const constant_modint& lhs, const constant_modint& rhs) { return constant_modint(lhs) += rhs; } friend constant_modint operator-(const constant_modint& lhs, const constant_modint& rhs) { return constant_modint(lhs) -= rhs; } friend constant_modint operator*(const constant_modint& lhs, const constant_modint& rhs) { return constant_modint(lhs) *= rhs; } friend constant_modint operator/(const constant_modint& lhs, const constant_modint& rhs) { return constant_modint(lhs) /= rhs; } friend bool operator==(const constant_modint& lhs, const constant_modint& rhs) { return lhs.val == rhs.val; } friend bool operator!=(const constant_modint& lhs, const constant_modint& rhs) { return lhs.val != rhs.val; } constant_modint pow(ull n) const { constant_modint res = 1, a = *this; while (n) { if (n&1) res *= a; a *= a; n >>= 1; } return res; } constant_modint inv() const { return inverse_mod(val, M); } friend std::ostream& operator<<(std::ostream& os, const constant_modint& m) { return os << m.val; } friend std::istream& operator>>(std::istream& is, constant_modint& m) { ll x; is >> x; m = constant_modint(x); return is; } }; template struct dynamic_modint { uint val; static uint& mod() { static uint M = 998244353; return M; } static void set_mod(uint m) { mod() = m; } dynamic_modint() : val(0) {} template dynamic_modint(T x) { ll y = x % (ll)mod(); if (y < 0) y += mod(); val = (uint)y; } dynamic_modint& operator+=(const dynamic_modint& rhs) { val += rhs.val; if (val >= mod()) val -= mod(); return *this; } dynamic_modint& operator-=(const dynamic_modint& rhs) { if (val < rhs.val) val += mod(); val -= rhs.val; return *this; } dynamic_modint& operator*=(const dynamic_modint& rhs) { val = (ull)val * rhs.val % mod(); return *this; } dynamic_modint& operator/=(const dynamic_modint& rhs) { return *this *= rhs.inv(); } dynamic_modint operator+() const { return *this; } dynamic_modint operator-() const { return dynamic_modint(0) - *this; } friend dynamic_modint operator+(const dynamic_modint& lhs, const dynamic_modint& rhs) { return dynamic_modint(lhs) += rhs; } friend dynamic_modint operator-(const dynamic_modint& lhs, const dynamic_modint& rhs) { return dynamic_modint(lhs) -= rhs; } friend dynamic_modint operator*(const dynamic_modint& lhs, const dynamic_modint& rhs) { return dynamic_modint(lhs) *= rhs; } friend dynamic_modint operator/(const dynamic_modint& lhs, const dynamic_modint& rhs) { return dynamic_modint(lhs) /= rhs; } friend bool operator==(const dynamic_modint& lhs, const dynamic_modint& rhs) { return lhs.val == rhs.val; } friend bool operator!=(const dynamic_modint& lhs, const dynamic_modint& rhs) { return lhs.val != rhs.val; } dynamic_modint pow(ull n) const { dynamic_modint res = 1, a = *this; while (n) { if (n & 1) res *= a; a *= a; n >>= 1; } return res; } dynamic_modint inv() const { ll a = val, b = mod(), u = 1, v = 0; while (b) { ll t = a / b; a -= t * b; swap(a, b); u -= t * v; swap(u, v); } return dynamic_modint(u); } friend std::ostream& operator<<(std::ostream& os, const dynamic_modint& m) { return os << m.val; } friend std::istream& operator>>(std::istream& is, dynamic_modint& m) { ll x; is >> x; m = dynamic_modint(x); return is; } }; #endif /* MODINT__HPP_ */ #ifndef INV_TABLE_HPP_ #define INV_TABLE_HPP_ #include using uint = unsigned; using ull = unsigned long long; using ll = long long; using namespace std; /// @brief mod M上での階乗,逆元テーブルを保持する構造体 /// @tparam M template class mod_table{ using mint = constant_modint; list invmodlist; list factorialmodlist; list factorialmodinvlist; uint N_MAX; public: constexpr mod_table(const uint N_MAX__){ N_MAX = max(1u, N_MAX__); invmodlist = list(N_MAX+1); factorialmodlist = list(N_MAX+1); factorialmodinvlist = list(N_MAX+1); invmodlist[1] = 1; for (uint i = 2; i <= N_MAX; i++){ invmodlist[i] = (M-M/i)*invmodlist[M%i]; } factorialmodinvlist[0] = 1; factorialmodlist[0] = 1; for (ull i = 1; i <= N_MAX; i++){ factorialmodinvlist[i] = invmodlist[i]*factorialmodinvlist[i-1]; factorialmodlist[i] = factorialmodlist[i-1]*i; } } inline constexpr mint inverse_mod(ll x) const { assert(0 <= x and x <= N_MAX); return invmodlist[x]; } inline constexpr mint factorialmod(ll x) const { assert(0 <= x and x <= N_MAX); return factorialmodlist[x]; } inline constexpr mint factorialmodinv(ll x) const { assert(0 <= x and x <= N_MAX); return factorialmodinvlist[x]; } inline constexpr mint inverse_mod(mint x) const {return inverse_mod(x.val);} inline constexpr mint factorialmod(mint x) const {return factorialmod(x.val);} inline constexpr mint factorialmodinv(mint x) const {return factorialmodinv(x.val);} }; #endif /* INV_TABLE_HPP_ */ using namespace std; using ll = long long; using uint = unsigned; #ifndef LEN_DEFINED #define LEN_DEFINED template inline constexpr ll len(const T& A){return ssize(A);} #endif template struct FormalPowerSeries{ using mint = constant_modint; list f; ll sz; constexpr FormalPowerSeries(){ sz = 0; } /// @brief 指定したサイズのfpsを作成する。 constexpr FormalPowerSeries(uint _init_sz) : f(_init_sz), sz(_init_sz) {} constexpr FormalPowerSeries(uint _init_sz, mint _init_val) : f(_init_sz,_init_val), sz(_init_sz) {} template constexpr FormalPowerSeries(const list& _init){ f.assign(_init.begin(), _init.end()); sz = f.size(); } template inline constexpr FormalPowerSeries(initializer_list _init) : FormalPowerSeries(list(_init)){} inline constexpr uint size() const {return sz;} inline constexpr bool empty() const {return size() == 0;} inline constexpr mint* data() {return empty() ? nullptr : &f[0];} inline constexpr const mint* data() const {return empty() ? nullptr : &f[0];} constexpr void resize(uint _new_size){ if (sz == _new_size){return;} f.resize(_new_size, 0); sz = _new_size; } inline constexpr mint& operator[](uint deg) {return f[deg];} inline constexpr const mint& operator[](uint deg) const {return f[deg];} template constexpr operator list() const { list res(sz); for (int i = 0; i < sz; i++){ res[i] = f[i].val; } return res; } constexpr void differential(){ if (sz == 0){return;} for (int i = 0; i < sz-1; i++){ f[i] = mint(i+1)*f[i+1]; } f.pop(); sz--; } constexpr void integral(const mod_table& mtable){ if (sz == 0){return;} sz++; f.append(0); for (int i = sz-1; i > 0; i--){ f[i] = mtable.inverse_mod(i)*f[i-1]; } f[0] = 0; } constexpr mint evaluate(const mint& x){ mint res = 0; for (int i = 0; i < sz; i++){ res *= x; res += f[sz-1-i]; } return res; } }; #endif /* FPS__HPP_ */ #ifndef PRIME_AND_DIVISORS_HPP_ #define PRIME_AND_DIVISORS_HPP_ #include #include #include #include #include using namespace std; using ll = long long; using ulll = __uint128_t; using pll = array; using pull = array; using pii = array; /// @brief 試し割り法で正の整数Nを素因数分解する /// @return list>{{素因数1,個数}, {素因数2,個数}, {素因数3,個数}...} template constexpr list> trial_division(T N){ assert(N > 0); if (N == 1){ return list> {{1,0}}; } list> R;//戻り値用リスト const T M = isqrt(static_cast(N)); for (T i = 2; i <= M; i++){ if (N % i == 0){ T divide_count = 0; while (N % i == 0){ divide_count++; N /= i; } R.append({i,divide_count}); } } if (N != 1){ R.append({N,1}); } return R; } /// @brief 素因数分解リストを受け取って約数関数の値を求める /// @return 約数のK乗和 template constexpr T divisor_function(const list>& vv, T K, const T MOD = -1){ if (vv[0][0] == 1){ return 1; } T R = 1; if (K == 0){ for (auto x : vv){ R *= x[1]+1; if (MOD > 0){ R %= MOD; } } } else{ for (auto x : vv){ ll r = powll(x[0],K); R *= (powll(r,x[1]+1) - 1)/(r - 1); if (MOD > 0){ R %= MOD; } } } return R; } /// @brief 素因数分解の結果pを受け取って、約数リストを生成する。 template constexpr list enumerate_divisor(const list>& p){ list d{1}; if (p[0][0] == 1){ return d; } for (auto &v : p){ T t = d.size(); T temp = 1; for (T w = 0; w < v[1]; w++){ temp *= v[0]; for (T i = 0; i < t; i++){ d.append(d[i]*temp); } } } sort(vall(d)); return d; } /// @brief 線形篩 /// @attention コンストラクタに整数Nを渡すことでN以下の整数を扱うことができる。 struct LinearSieve{ list p_list; list lpf; //Nを渡すことで1以上N以下の整数を扱うことができる LinearSieve(int N): lpf(N+1,-1){ lpf[1] = 1; int p_list_size = 0; for (int i = 2; i <= N; i++){ if (lpf[i] < 0){ p_list.append(i); p_list_size++; lpf[i] = i; } for (int j = 0; j < p_list_size && p_list[j] <= lpf[i] && p_list[j]*i <= N; j++){ lpf[p_list[j]*i] = p_list[j]; } } } list factorize(int x){ if (x == 1){return {{1,0}};} list r; do{ if (r.empty() || lpf[x] != r.back()[0]){ r.append({lpf[x], 1}); } else{ r.back()[1]++; } x /= lpf[x]; }while(x > 1); return r; } list> factorize_all(int N){ list> r(N+1); r[1].append({1,0}); for (int i = 2; i <= N; i++){ r[i] = factorize(i); } return r; } /// @brief N以下の整数に対するメビウス関数の値を列挙する。 list enumerate_mobius(int N){ list ret(N+1); ret[1] = 1; for (int i = 2; i <= N; i++){ if (lpf[i] == i){ret[i] = -1; continue;} int temp = i/lpf[i]; if (lpf[i] == lpf[temp]){ ret[i] = 0; } else{ ret[i] = ret[lpf[i]]*ret[temp]; } } return ret; } }; constexpr ull __MillerRabin_small[3] = {2,7,61}; constexpr ull __MillerRabin_large[7] = {2,325,9375,28178,450775,9780504,1795265022}; struct Montgomery64 { ull n, ni, r2; constexpr Montgomery64(ull n) : n(n), ni(n), r2(-ulll(n) % n){ for (int i = 0; i < 5; ++i) ni *= 2ull - n * ni; ni *= -1; } constexpr ull reduce(ulll x) const { ull m = (ull)x * ni; ull res = (x + ulll(m) * n) >> 64; return res >= n ? res - n : res; } constexpr ull mul(ull x, ull y) const { return reduce(ulll(x) * y); } constexpr ull pow_not_reduced(ull a, ull b)const{ ull res = reduce(r2); a = reduce(ulll(a) * r2); while (b) { if (b & 1) res = mul(res, a); a = mul(a, a); b >>= 1; } return res; } }; ///@brief ミラーラビン素数判定法 template constexpr bool MillerRabin(T N){ if (N <= 1) return false; if (N == 2 || N == 3) return true; if (N % 2 == 0) return false; T d = N - 1; T s = 0; while (d % 2 == 0){ d >>= 1; s++; } auto modpow_T = [&](T a,T b,T m){ T t = a%m; T ans = (m == 1 ? 0 : 1); while (b > 0){ if (b&1){ ans = (ans*t)%m; } b >>= 1; t = (t*t)%m; } return ans; }; auto check = [&](T a){ if (a >= N) a %= N; if (a == 0) return true; T x = modpow_T(a, d, N); if (x == 1 || x == N-1) return true; for (int r = 1; r < s; r++){ x = x*x%N; if (x == N - 1) return true; } return false; }; if (N < 4759123141){ for (auto a : __MillerRabin_small){ if (!check(static_cast(a))) return false; } } else if (N < 1000000000000000000){ for (auto a : __MillerRabin_large){ if (!check(static_cast(a))) return false; } } else{ for (auto a : __MillerRabin_large){ if (!check(static_cast(a))) return false; } //50個の整数でテスト for (T a = 638245987265732141; a < 638245987265732191; a++){ if (!check(a)) return false; } } return true; } /// @brief ミラーラビン素数判定法のull範囲限定版 template<> constexpr bool MillerRabin(ull N){ if (N <= 1) return false; if (N == 2 || N == 3) return true; if (N % 2 == 0) return false; ull d = N - 1; int s = 0; while (d % 2 == 0){ d >>= 1; s++; } Montgomery64 mg(N); auto check = [&](ull a){ if (a >= N) a %= N; if (a == 0) return true; ull x = mg.pow_not_reduced(a, d); ull y = mg.reduce(x); if (y == 1 || y == N-1) return true; for (int r = 1; r < s; r++){ x = mg.mul(x, x); if (mg.reduce(x) == N - 1) return true; } return false; }; if (N < 4759123141ull){ for (auto a : __MillerRabin_small){ if (!check(a)) return false; } } else{ for (auto a : __MillerRabin_large){ if (!check(a)) return false; } } return true; } /// @brief ポラード・ロー法による素因数分解 template constexpr list> Pollard_rho(T N){ assert(N > 0); if (N == 1){return {{1,0}};} list res; list dq; dq.append(N); while (!dq.empty()){ T n = dq.back(); dq.pop(); bool next_loop = false; while (!MillerRabin(n)){ if ((n&1) == 0){ while ((n&1) == 0){res.append(2); n>>=1;} if (n == 1){next_loop = true; break;} continue; } T blocksize = sqrt(sqrt(sqrt(n))); for (T c = 1; c <= n; c++){ T y = (0b1010110101101011110011ull^c^n)%n; T r = 1; T k = 0; T k_mod_blocksize = 0; bool next_loop_inner = false; for (int _ = 0;;){ T q = 1; T q_old = q; T y_old = y; T x = y; while (k < r){ k++; k_mod_blocksize++; y = y*y%n+c; if (y >= n){ y -= n;} q = q*(y >= x ? (y-x) : (x-y))%n; if (k_mod_blocksize == blocksize){ k_mod_blocksize -= blocksize; T g = gcd(q,n); if (g > 1){ if (g != n){ dq.append(g); dq.append(n/g); next_loop = true; break; } else{ y = y_old; q = q_old; for (int i = 0; i < blocksize; i++){ y = y*y%n+c; if (y >= n){ y -= n;} q = q*(y >= x ? (y-x) : (x-y))%n; g = gcd(q,n); if (g > 1){ if (g != n){ dq.append(g); dq.append(n/g); next_loop = true; break; } next_loop_inner = true; break; } } if (next_loop || next_loop_inner) break; } } q_old = q; y_old = y; } } if (next_loop || next_loop_inner) break; q_old = gcd(q,n); if (q_old > 1){ if (q_old != n){ dq.append(q_old); dq.append(n/q_old); next_loop = true; break; } next_loop_inner = true; break; } r <<= 1; } if (next_loop) break; } if (next_loop) break; } if (!next_loop) { res.append(n); } } sort(res.begin(), res.end()); list> res2; for (ull l = 0, r = 0, sz = res.size(); l < sz;){ while (r < sz && res[l] == res[r]){ r++; } res2.append({res[l], r-l}); l = r; } return res2; } /// @brief ポラード・ロー法による素因数分解のull範囲限定版 template<> constexpr list Pollard_rho(ull N){ assert(N > 0); if (N == 1){return {{1,0}};} list res; list dq; dq.append(N); while (!dq.empty()){ ull n = dq.back(); dq.pop(); bool next_loop = false; while (!MillerRabin(n)){ if ((n&1) == 0){ while ((n&1) == 0){res.append(2); n>>=1;} if (n == 1){next_loop = true; break;} continue; } Montgomery64 mg(n); int blocksize = max(1, pow(n, 0.125)); for (ull c = 1; c <= n; c++){ ull reduced_c = mg.reduce(c*(ulll)mg.r2); ull y = (0b1010110101101011110011ull^c^n)%n; y = mg.reduce(y*(ulll)mg.r2); ull r = 1; ull k = 0; int k_mod_blocksize = 0; bool next_loop_inner = false; for (int _ = 0; _ < 63; _++){ ull q = mg.reduce(mg.r2); ull q_old = q; ull y_old = y; ull x = y; while (k < r){ k++; k_mod_blocksize++; y = mg.reduce(y*(ulll)y)+reduced_c; if (y >= n){ y -= n;} q = mg.reduce(q*(y >= x ? (ulll)(y-x) : (ulll)(x-y))); if (k_mod_blocksize == blocksize){ k_mod_blocksize -= blocksize; ull g = gcd(mg.reduce(q),n); if (g > 1){ if (g != n){ dq.append(g); dq.append(n/g); next_loop = true; break; } else{ y = y_old; q = q_old; for (int i = 0; i < blocksize; i++){ y = mg.reduce(y*(ulll)y)+reduced_c; if (y >= n){ y -= n;} q = mg.reduce(q*(y >= x ? (ulll)(y-x) : (ulll)(x-y))); g = gcd(mg.reduce(q),n); if (g > 1){ if (g != n){ dq.append(g); dq.append(n/g); next_loop = true; break; } next_loop_inner = true; break; } } if (next_loop || next_loop_inner) break; } } q_old = q; y_old = y; } } if (next_loop || next_loop_inner) break; q_old = gcd(mg.reduce(q),n); if (q_old > 1){ if (q_old != n){ dq.append(q_old); dq.append(n/q_old); next_loop = true; break; } next_loop_inner = true; break; } r <<= 1; } if (next_loop) break; } if (next_loop) break; } if (!next_loop) { res.append(n); } } sort(res.begin(), res.end()); list res2; for (int l = 0, r = 0, sz = res.size(); l < sz;){ while (r < sz && res[l] == res[r]){ r++; } res2.append({res[l], static_cast(r-l)}); l = r; } return res2; } /// @brief 素因数分解を行う。`N`の大きさによって、試し割り法とポラード・ロー法を使い分けてくれる。 template constexpr list> factorize(T N){ if (N < 640000){ return trial_division(N); } if (N < 1000000000000000000){ return Pollard_rho(N); } return Pollard_rho(N); } template<> constexpr list factorize(ull N){ if (N < 640000ull){ return trial_division(N); } return Pollard_rho(N); } template<> constexpr list factorize(ll N){ auto res = factorize(N); list res2(res.size()); for (int i = 0, sz = res.size(); i < sz; i++){ res2[i][0] = res[i][0]; res2[i][1] = res[i][1]; } return res2; } template<> constexpr list factorize(int N){ auto res = factorize(N); list res2(res.size()); for (int i = 0, sz = res.size(); i < sz; i++){ res2[i][0] = res[i][0]; res2[i][1] = res[i][1]; } return res2; } struct Xorshift64{ ull state; constexpr Xorshift64(ull seed = 881726454633252252ull) : state(seed) {} constexpr ull next() { state ^= state << 13; state ^= state >> 7; state ^= state << 17; return state; } }; /// @brief mod Pにおける原始根を1つ探す。Pはull範囲内の素数でなければならない。 constexpr ull primitive_root(ull P){ assert(MillerRabin(P)); if (P == 2){ return 1; } Montgomery64 mg(P); Xorshift64 rng(0x1234567E89ABCDEF); auto factorized = factorize(P-1); while (true) { ull r = rng.next()%P; if (r == 0){continue;} bool ok = true; for (auto& q : factorized){ if (mg.reduce(mg.pow_not_reduced(r, (P-1)/q[0])) == 1){ ok = false; break; } } if (ok) return r; } } #endif /* PRIME_AND_DIVISORS_HPP_ */ using namespace std; using ll = long long; using uint = unsigned; using ull = unsigned long long; #define vall(A) A.begin(), A.end() template inline bool btest_for_fps(T K, int i){return K&(1ull< struct fps_operator{ template using fps = FormalPowerSeries; template using mint = constant_modint; uint sum_e[30]; uint sum_ie[30]; uint log_max_length; uint last_powroot; // --- モンゴメリ乗算用の定数と関数 --- static constexpr uint get_r() { uint res = M; for (int i = 0; i < 4; ++i) res *= 2 - M * res; return res; } static constexpr uint R = get_r(); static constexpr uint R2 = -ull(M) % M; static constexpr uint NEG_INV = 0 - R; static inline constexpr uint reduce(ull x) { uint res = (x + ull(uint(x) * NEG_INV) * M) >> 32; return res >= M ? res - M : res; } static inline constexpr uint to_montgomery(uint x) { return reduce(ull(x) * R2); } static inline constexpr uint from_montgomery(uint x) { return reduce(x); } static inline constexpr uint montgomery_mul(uint x, uint y) { return reduce(ull(x) * y); } static inline constexpr uint mod_add(uint x, uint y) { return x + y >= M ? x + y - M : x + y; } static inline constexpr uint mod_sub(uint x, uint y) { return x < y ? x + M - y : x - y; } // --- AVX-512 / AVX2 SIMD Intrinsics --- #if defined(__AVX512F__) && defined(__AVX512DQ__) static inline __m512i montgomery_mul_simd(__m512i a, __m512i b, __m512i mod, __m512i neg_inv) { __m512i mul0 = _mm512_mul_epu32(a, b); __m512i mul1 = _mm512_mul_epu32(_mm512_srli_epi64(a, 32), _mm512_srli_epi64(b, 32)); __m512i q0 = _mm512_mul_epu32(_mm512_mullo_epi32(mul0, neg_inv), mod); __m512i q1 = _mm512_mul_epu32(_mm512_mullo_epi32(mul1, neg_inv), mod); __m512i res0 = _mm512_srli_epi64(_mm512_add_epi64(mul0, q0), 32); __m512i res1 = _mm512_srli_epi64(_mm512_add_epi64(mul1, q1), 32); __m512i res = _mm512_or_si512(res0, _mm512_slli_epi64(res1, 32)); __mmask16 cmp = _mm512_cmpge_epu32_mask(res, mod); return _mm512_mask_sub_epi32(res, cmp, res, mod); } static inline __m512i mod_add_simd(__m512i a, __m512i b, __m512i mod) { __m512i res = _mm512_add_epi32(a, b); __mmask16 cmp = _mm512_cmpge_epu32_mask(res, mod); return _mm512_mask_sub_epi32(res, cmp, res, mod); } static inline __m512i mod_sub_simd(__m512i a, __m512i b, __m512i mod) { __mmask16 cmp = _mm512_cmplt_epu32_mask(a, b); __m512i res = _mm512_sub_epi32(a, b); return _mm512_mask_add_epi32(res, cmp, res, mod); } #elif defined(__AVX2__) // AVX2用 モンゴメリ乗算・剰余加減算 static inline __m256i montgomery_mul_simd(__m256i a, __m256i b, __m256i mod, __m256i neg_inv) { __m256i mul0 = _mm256_mul_epu32(a, b); __m256i mul1 = _mm256_mul_epu32(_mm256_srli_epi64(a, 32), _mm256_srli_epi64(b, 32)); __m256i q0 = _mm256_mul_epu32(_mm256_mullo_epi32(mul0, neg_inv), mod); __m256i q1 = _mm256_mul_epu32(_mm256_mullo_epi32(mul1, neg_inv), mod); __m256i res0 = _mm256_srli_epi64(_mm256_add_epi64(mul0, q0), 32); __m256i res1 = _mm256_srli_epi64(_mm256_add_epi64(mul1, q1), 32); __m256i res = _mm256_blend_epi32(res0, _mm256_slli_epi64(res1, 32), 0xAA); __m256i diff = _mm256_sub_epi32(res, mod); // diff < 0 なら -1 (全ビット1)、そうでないなら 0 となるマスク __m256i mask = _mm256_srai_epi32(diff, 31); return _mm256_add_epi32(diff, _mm256_and_si256(mask, mod)); } static inline __m256i mod_add_simd(__m256i a, __m256i b, __m256i mod) { __m256i diff = _mm256_sub_epi32(_mm256_add_epi32(a, b), mod); __m256i mask = _mm256_srai_epi32(diff, 31); return _mm256_add_epi32(diff, _mm256_and_si256(mask, mod)); } static inline __m256i mod_sub_simd(__m256i a, __m256i b, __m256i mod) { __m256i diff = _mm256_sub_epi32(a, b); __m256i mask = _mm256_srai_epi32(diff, 31); return _mm256_add_epi32(diff, _mm256_and_si256(mask, mod)); } #endif constexpr fps_operator(){ list powroot{1}; list powrootinv; while (powroot.back() >= 0){ powroot.append(cipolla(powroot.back())); if (powroot.back() == powroot[powroot.size()-2]){ powroot.back() = M-powroot.back(); } } powroot.pop(); log_max_length = powroot.size()-1; last_powroot = powroot.back(); for (auto v : powroot){ powrootinv.append(inverse_mod(v,M)); } int cnt2 = powroot.size()-1; uint now = reduce(R2); for (int i = 0; i <= cnt2-2; i++){ sum_e[i] = montgomery_mul(to_montgomery(powroot[i+2]), now); now = montgomery_mul(now, to_montgomery(powrootinv[i+2])); } uint inow = reduce(R2); for (int i = 0; i <= cnt2-2; i++){ sum_ie[i] = montgomery_mul(to_montgomery(powrootinv[i+2]), inow); inow = montgomery_mul(inow, to_montgomery(powroot[i+2])); } for (int i = cnt2-1; i < 30; i++){ sum_e[i] = 0; sum_ie[i] = 0; } } // Public transforms return canonical residues in the original bit-reversed // order. IDFT remains unnormalized. Transposes share the same kernels. private: template void transform_scalar(mint* a, int n) const { const uint* rate = InverseRoots ? sum_ie : sum_e; const int h = __builtin_ctz(unsigned(n)); for (int stage = 0; stage < h; ++stage) { const int p = InverseButterfly ? 1 << stage : n >> (stage + 1); uint w = to_montgomery(1); for (int offset = 0, s = 0; offset < n; offset += 2*p, ++s) { for (int i = 0; i < p; ++i) { const uint l = a[offset+i].val, r = a[offset+i+p].val; if constexpr (InverseButterfly) { a[offset+i].val = mod_add(l, r); a[offset+i+p].val = montgomery_mul(mod_sub(l, r), w); } else { const uint t = montgomery_mul(r, w); a[offset+i].val = mod_add(l, t); a[offset+i+p].val = mod_sub(l, t); } } w = montgomery_mul(w, rate[__builtin_ctz(~unsigned(s))]); } } } #if defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__)) // Internal SIMD residues are in [0, 2M). M < 2^30 guarantees that // additions fit uint32_t and Montgomery products reduce to [0, 2M). __attribute__((target("avx2"))) static inline __m256i mul_lazy256(__m256i a, __m256i b) { const __m256i mod = _mm256_set1_epi32(M); const __m256i ni = _mm256_set1_epi32(NEG_INV); const __m256i x = _mm256_mul_epu32(a, b); const __m256i y = _mm256_mul_epu32(_mm256_srli_epi64(a, 32), _mm256_srli_epi64(b, 32)); // Only the low 32 bits of each 64-bit lane are consumed below; a // widening multiply avoids computing the unused odd 32-bit products. const __m256i qx = _mm256_mul_epu32(_mm256_mul_epu32(x, ni), mod); const __m256i qy = _mm256_mul_epu32(_mm256_mul_epu32(y, ni), mod); const __m256i rx = _mm256_srli_epi64(_mm256_add_epi64(x, qx), 32); const __m256i ry = _mm256_add_epi64(y, qy); const __m256i r = _mm256_blend_epi32(rx, ry, 0xaa); return r; } __attribute__((target("avx2"))) static inline __m256i mul256(__m256i a, __m256i b) { const __m256i r = mul_lazy256(a,b); return _mm256_min_epu32(r,_mm256_sub_epi32(r,_mm256_set1_epi32(M))); } __attribute__((target("avx2"))) static inline __m256i add_lazy256(__m256i a, __m256i b) { const __m256i r = _mm256_add_epi32(a, b); return _mm256_min_epu32(r, _mm256_sub_epi32(r, _mm256_set1_epi32(2*M))); } __attribute__((target("avx2"))) static inline __m256i sub_lazy256(__m256i a, __m256i b) { const __m256i r = _mm256_sub_epi32(a, b); return _mm256_min_epu32(r, _mm256_add_epi32(r, _mm256_set1_epi32(2*M))); } __attribute__((target("avx2"))) static inline __m256i shoup256(__m256i a, __m256i b, __m256i bq) { // b is an ordinary residue; bq = floor(b*2^32/M). The result is // congruent to a*b and lies in [0, 2M), without a final correction. const __m256i q0 = _mm256_srli_epi64(_mm256_mul_epu32(a,bq),32); const __m256i q1 = _mm256_mul_epu32(_mm256_srli_epi64(a,32),_mm256_srli_epi64(bq,32)); const __m256i q = _mm256_blend_epi32(q0,q1,0xaa); const __m256i mod = _mm256_set1_epi32(M); const __m256i r = _mm256_sub_epi32(_mm256_mullo_epi32(a,b),_mm256_mullo_epi32(q,mod)); return r; } template __attribute__((target("avx2"))) void transform_avx2(mint* a, int n) const { const uint* rate = InverseRoots ? sum_ie : sum_e; const int h = __builtin_ctz(unsigned(n)); uint rate4[29]; for (int i = 0; i+1 < h; ++i) rate4[i] = montgomery_mul(rate[0],rate[i+1]); const __m256i imag = _mm256_set1_epi32(rate[0]); const uint im = from_montgomery(rate[0]); const __m256i imv = _mm256_set1_epi32(im); const __m256i imq = _mm256_set1_epi32((ull(im)<<32)/M); int first_stage = 0; if constexpr (ZeroUpperHalf && !InverseButterfly) { // The first radix-4 block has no twiddle and its upper two inputs // are zero. Do not load padding or execute the redundant butterflies. const int p = n/4; for (int i = 0; i < p; i += 8) { const __m256i x = _mm256_loadu_si256((const __m256i*)(a+i)); const __m256i y = _mm256_loadu_si256((const __m256i*)(a+i+p)); const __m256i t = shoup256(y,imv,imq); _mm256_storeu_si256((__m256i*)(a+i),add_lazy256(x,y)); _mm256_storeu_si256((__m256i*)(a+i+p),sub_lazy256(x,y)); _mm256_storeu_si256((__m256i*)(a+i+2*p),add_lazy256(x,t)); _mm256_storeu_si256((__m256i*)(a+i+3*p),sub_lazy256(x,t)); } first_stage = 2; } for (int stage = first_stage; stage < h; ++stage) { const int p4 = InverseButterfly ? 1 << stage : n >> (stage + 2); if (stage + 1 < h && p4 >= 8) { uint w = to_montgomery(1); for (int offset = 0, s = 0; offset < n; offset += 4*p4, ++s) { const uint w2 = montgomery_mul(w,w), w3 = montgomery_mul(w2,w); const uint b1 = from_montgomery(w), b2 = from_montgomery(w2), b3 = from_montgomery(w3); const __m256i v1 = _mm256_set1_epi32(b1), q1 = _mm256_set1_epi32((ull(b1)<<32)/M); const __m256i v2 = _mm256_set1_epi32(b2), q2 = _mm256_set1_epi32((ull(b2)<<32)/M); const __m256i v3 = _mm256_set1_epi32(b3), q3 = _mm256_set1_epi32((ull(b3)<<32)/M); for (int i = 0; i < p4; i += 8) { __m256i a0 = _mm256_loadu_si256((const __m256i*)(a+offset+i)); __m256i a1 = _mm256_loadu_si256((const __m256i*)(a+offset+i+p4)); __m256i a2 = _mm256_loadu_si256((const __m256i*)(a+offset+i+2*p4)); __m256i a3 = _mm256_loadu_si256((const __m256i*)(a+offset+i+3*p4)); __m256i x,y,z,t; if constexpr (InverseButterfly) { x = add_lazy256(a0,a1); y = sub_lazy256(a0,a1); z = add_lazy256(a2,a3); t = shoup256(sub_lazy256(a2,a3),imv,imq); a0 = add_lazy256(x,z); a1 = add_lazy256(y,t); a2 = sub_lazy256(x,z); a3 = sub_lazy256(y,t); if (s != 0) { a1 = shoup256(a1,v1,q1); a2 = shoup256(a2,v2,q2); a3 = shoup256(a3,v3,q3); } } else { if (s != 0) { a1 = shoup256(a1,v1,q1); a2 = shoup256(a2,v2,q2); a3 = shoup256(a3,v3,q3); } x = add_lazy256(a0,a2); y = add_lazy256(a1,a3); z = sub_lazy256(a0,a2); t = shoup256(sub_lazy256(a1,a3),imv,imq); a0 = add_lazy256(x,y); a1 = sub_lazy256(x,y); a2 = add_lazy256(z,t); a3 = sub_lazy256(z,t); } _mm256_storeu_si256((__m256i*)(a+offset+i),a0); _mm256_storeu_si256((__m256i*)(a+offset+i+p4),a1); _mm256_storeu_si256((__m256i*)(a+offset+i+2*p4),a2); _mm256_storeu_si256((__m256i*)(a+offset+i+3*p4),a3); } w = montgomery_mul(w,rate4[__builtin_ctz(~unsigned(s))]); } ++stage; continue; } if (stage + 1 < h && n >= 32) { const int p = p4, groups = 8/p; alignas(32) uint lane1[8], lane2[8], lane3[8]; uint w = to_montgomery(1), last = w; for (int s = 0; s < groups; ++s) { const uint w2 = montgomery_mul(w,w), w3 = montgomery_mul(w2,w); for (int i = 0; i < p; ++i) { const int j = p == 1 ? (s/2 + (s%2)*4) : s*p+i; lane1[j] = w; lane2[j] = w2; lane3[j] = w3; } last = w; w = montgomery_mul(w,rate4[__builtin_ctz(~unsigned(s))]); } __m256i v1 = _mm256_load_si256((const __m256i*)lane1); __m256i v2 = _mm256_load_si256((const __m256i*)lane2); __m256i v3 = _mm256_load_si256((const __m256i*)lane3); const int shift = __builtin_ctz(unsigned(groups)); uint advance1[30], advance2[30], advance3[30]; for (int i = 0; i+shift+1 < h; ++i) { const uint d = montgomery_mul(last,rate4[i+shift]); advance1[i] = d; advance2[i] = montgomery_mul(d,d); advance3[i] = montgomery_mul(advance2[i],d); } for (int offset = 0, s = 0; offset < n; offset += 32, ++s) { __m256i x0 = _mm256_loadu_si256((const __m256i*)(a+offset)); __m256i x1 = _mm256_loadu_si256((const __m256i*)(a+offset+8)); __m256i x2 = _mm256_loadu_si256((const __m256i*)(a+offset+16)); __m256i x3 = _mm256_loadu_si256((const __m256i*)(a+offset+24)); __m256i a0,a1,a2,a3; if (p == 1) { const __m256i t0 = _mm256_unpacklo_epi32(x0,x1), t1 = _mm256_unpackhi_epi32(x0,x1); const __m256i t2 = _mm256_unpacklo_epi32(x2,x3), t3 = _mm256_unpackhi_epi32(x2,x3); a0 = _mm256_unpacklo_epi64(t0,t2); a1 = _mm256_unpackhi_epi64(t0,t2); a2 = _mm256_unpacklo_epi64(t1,t3); a3 = _mm256_unpackhi_epi64(t1,t3); } else if (p == 2) { const __m256i t0 = _mm256_unpacklo_epi64(x0,x1), t1 = _mm256_unpackhi_epi64(x0,x1); const __m256i t2 = _mm256_unpacklo_epi64(x2,x3), t3 = _mm256_unpackhi_epi64(x2,x3); a0 = _mm256_permute2x128_si256(t0,t2,0x20); a1 = _mm256_permute2x128_si256(t1,t3,0x20); a2 = _mm256_permute2x128_si256(t0,t2,0x31); a3 = _mm256_permute2x128_si256(t1,t3,0x31); } else { a0 = _mm256_permute2x128_si256(x0,x2,0x20); a1 = _mm256_permute2x128_si256(x0,x2,0x31); a2 = _mm256_permute2x128_si256(x1,x3,0x20); a3 = _mm256_permute2x128_si256(x1,x3,0x31); } __m256i x,y,z,t; if constexpr (InverseButterfly) { x = add_lazy256(a0,a1); y = sub_lazy256(a0,a1); z = add_lazy256(a2,a3); t = mul_lazy256(sub_lazy256(a2,a3),imag); a0 = add_lazy256(x,z); a1 = mul_lazy256(add_lazy256(y,t),v1); a2 = mul_lazy256(sub_lazy256(x,z),v2); a3 = mul_lazy256(sub_lazy256(y,t),v3); } else { a1 = mul_lazy256(a1,v1); a2 = mul_lazy256(a2,v2); a3 = mul_lazy256(a3,v3); x = add_lazy256(a0,a2); y = add_lazy256(a1,a3); z = sub_lazy256(a0,a2); t = mul_lazy256(sub_lazy256(a1,a3),imag); a0 = add_lazy256(x,y); a1 = sub_lazy256(x,y); a2 = add_lazy256(z,t); a3 = sub_lazy256(z,t); } if (p == 1) { const __m256i t0 = _mm256_unpacklo_epi32(a0,a1), t1 = _mm256_unpackhi_epi32(a0,a1); const __m256i t2 = _mm256_unpacklo_epi32(a2,a3), t3 = _mm256_unpackhi_epi32(a2,a3); x0 = _mm256_unpacklo_epi64(t0,t2); x1 = _mm256_unpackhi_epi64(t0,t2); x2 = _mm256_unpacklo_epi64(t1,t3); x3 = _mm256_unpackhi_epi64(t1,t3); } else if (p == 2) { const __m256i t0 = _mm256_permute2x128_si256(a0,a2,0x20), t1 = _mm256_permute2x128_si256(a1,a3,0x20); const __m256i t2 = _mm256_permute2x128_si256(a0,a2,0x31), t3 = _mm256_permute2x128_si256(a1,a3,0x31); x0 = _mm256_unpacklo_epi64(t0,t1); x1 = _mm256_unpackhi_epi64(t0,t1); x2 = _mm256_unpacklo_epi64(t2,t3); x3 = _mm256_unpackhi_epi64(t2,t3); } else { x0 = _mm256_permute2x128_si256(a0,a1,0x20); x1 = _mm256_permute2x128_si256(a2,a3,0x20); x2 = _mm256_permute2x128_si256(a0,a1,0x31); x3 = _mm256_permute2x128_si256(a2,a3,0x31); } _mm256_storeu_si256((__m256i*)(a+offset),x0); _mm256_storeu_si256((__m256i*)(a+offset+8),x1); _mm256_storeu_si256((__m256i*)(a+offset+16),x2); _mm256_storeu_si256((__m256i*)(a+offset+24),x3); const int k = __builtin_ctz(~unsigned(s)); v1 = mul256(v1,_mm256_set1_epi32(advance1[k])); v2 = mul256(v2,_mm256_set1_epi32(advance2[k])); v3 = mul256(v3,_mm256_set1_epi32(advance3[k])); } ++stage; continue; } const int p = InverseButterfly ? 1 << stage : n >> (stage + 1); uint w = to_montgomery(1); if (p >= 8) { for (int offset = 0, s = 0; offset < n; offset += 2*p, ++s) { const __m256i vw = _mm256_set1_epi32(w); for (int i = 0; i < p; i += 8) { __m256i l = _mm256_loadu_si256((const __m256i*)(a+offset+i)); __m256i r = _mm256_loadu_si256((const __m256i*)(a+offset+i+p)); __m256i x, y; if constexpr (InverseButterfly) { x = add_lazy256(l, r); y = mul_lazy256(sub_lazy256(l, r), vw); } else { r = mul_lazy256(r, vw); x = add_lazy256(l, r); y = sub_lazy256(l, r); } _mm256_storeu_si256((__m256i*)(a+offset+i), x); _mm256_storeu_si256((__m256i*)(a+offset+i+p), y); } w = montgomery_mul(w, rate[__builtin_ctz(~unsigned(s))]); } } else { // Pack 8 butterflies even when each block has only 1, 2 or 4 pairs. alignas(32) uint lane[8]{}; uint t = to_montgomery(1), last = t; const int groups = 8/p; for (int s = 0; s < groups; ++s) { for (int i = 0; i < p; ++i) lane[s*p+i] = t; last = t; t = montgomery_mul(t, rate[__builtin_ctz(~unsigned(s))]); } const __m256i base = _mm256_load_si256((const __m256i*)lane); const __m256i unpack = p == 1 ? _mm256_setr_epi32(0,2,4,6,1,3,5,7) : _mm256_setr_epi32(0,1,4,5,2,3,6,7); const __m256i pack = p == 1 ? _mm256_setr_epi32(0,4,1,5,2,6,3,7) : unpack; const int shift = __builtin_ctz(unsigned(groups)); uint advance[30]; for (int i = 0; i + shift < h; ++i) advance[i] = montgomery_mul(last, rate[i+shift]); for (int offset = 0, s = 0; offset < n; offset += 16, ++s) { __m256i x = _mm256_loadu_si256((const __m256i*)(a+offset)); __m256i y = _mm256_loadu_si256((const __m256i*)(a+offset+8)); if (p != 4) { x = _mm256_permutevar8x32_epi32(x, unpack); y = _mm256_permutevar8x32_epi32(y, unpack); } __m256i l = _mm256_permute2x128_si256(x,y,0x20); __m256i r = _mm256_permute2x128_si256(x,y,0x31); const __m256i vw = mul256(base, _mm256_set1_epi32(w)); if constexpr (InverseButterfly) { x = add_lazy256(l,r); y = mul_lazy256(sub_lazy256(l,r),vw); } else { r = mul_lazy256(r,vw); x = add_lazy256(l,r); y = sub_lazy256(l,r); } l = _mm256_permute2x128_si256(x,y,0x20); r = _mm256_permute2x128_si256(x,y,0x31); if (p != 4) { l = _mm256_permutevar8x32_epi32(l,pack); r = _mm256_permutevar8x32_epi32(r,pack); } _mm256_storeu_si256((__m256i*)(a+offset),l); _mm256_storeu_si256((__m256i*)(a+offset+8),r); w = montgomery_mul(w,advance[__builtin_ctz(~unsigned(s))]); } } } const __m256i mod = _mm256_set1_epi32(M); if constexpr (Canonical) for (int i = 0; i < n; i += 8) { __m256i x = _mm256_loadu_si256((const __m256i*)(a+i)); x = _mm256_min_epu32(x,_mm256_sub_epi32(x,mod)); _mm256_storeu_si256((__m256i*)(a+i),x); } } template __attribute__((target("avx2"))) static void multiply_avx2(mint* dst, const mint* a, const mint* b, int n, uint scale) { const __m256i vs = _mm256_set1_epi32(scale); for (int i = 0; i < n; i += 8) { __m256i x = _mm256_loadu_si256((const __m256i*)(a+i)); const __m256i y = _mm256_loadu_si256((const __m256i*)(b+i)); x = mul_lazy256(mul_lazy256(x,y),vs); if constexpr (Canonical) x = _mm256_min_epu32(x,_mm256_sub_epi32(x,_mm256_set1_epi32(M))); _mm256_storeu_si256((__m256i*)(dst+i),x); } } #endif static bool use_avx2() { #if defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__)) // This check also makes ordinary -O2/-O3 builds use SIMD on supported CPUs. #ifdef FPS_DISABLE_SIMD return false; #else return M < (1u << 30) && __builtin_cpu_supports("avx2"); #endif #else return false; #endif } // Pointwise product with optional normalization, all operands in ordinary // representation. dst may alias either input. void pointwise_product(fps& dst, const fps& lhs, const fps& rhs, int n, mint factor = 1) const { if (n == 0) return; auto* out = &dst[0]; const auto* a = &lhs[0]; const auto* b = &rhs[0]; int i = 0; #if defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__)) if (use_avx2()) { i = n & ~7; multiply_avx2(out,a,b,i,ull(R2)*factor.val%M); } #endif for (; i < n; ++i) out[i] = a[i]*b[i]*factor; } template void transform(fps& F) const { if (F.empty()) return; F.f.reserve(upperpow2(F.sz)); F.resize(upperpow2(F.sz)); const int n = F.size(); assert(unsigned(n) <= (1u << log_max_length)); auto* a = &F[0]; #if defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__)) if (n >= 16 && use_avx2()) { transform_avx2(a,n); return; } #endif transform_scalar(a,n); } public: void inplaceDFT(fps& F) const { transform(F); } void inplaceIDFT(fps& F) const { transform(F); } void inplaceDFT_T(fps& F) const { transform(F); } void inplaceIDFT_T(fps& F) const { transform(F); } private: static fps convolution_copy(const fps& src, int capacity) { assert(capacity > 0 && src.size() <= unsigned(capacity)); fps result(capacity); result.resize(src.size()); if (!src.empty()) std::copy_n(src.data(),src.size(),result.data()); return result; } static int convolution_capacity(const fps& a, const fps& b) { if (a.empty() || b.empty() || std::min(a.size(),b.size()) <= 60) return 0; if (std::max(a.size(),b.size())/std::min(a.size(),b.size()) >= 16) return 0; return upperpow2(a.size()+b.size()-1); } fps convolution_owned(fps F1, fps F2) const { int n = F1.size(), m = F2.size(); if (n == 0 || m == 0) return fps(0); if (std::min(n,m) <= 60) { if (n < m) { std::swap(n,m); std::swap(F1,F2); } fps ans(n+m-1); auto* c = &ans[0]; const auto* a = &F1[0]; const auto* b = &F2[0]; for (int i = 0; i < n; ++i) for (int j = 0; j < m; ++j) c[i+j] += a[i]*b[j]; return ans; } if (std::max(n,m)/std::min(n,m) >= 16) { if (n < m) { std::swap(n,m); std::swap(F1,F2); } // Overlap-add: reuse the short operand's transform for each block // instead of padding it to the length of the entire long operand. const int length = upperpow2(2*m-1), step = length-m+1; F2.f.reserve(length); F2.resize(length); inplaceDFT(F2); fps block(length), ans(n+m-1); const mint scale = mint(length).inv(); for (int start = 0; start < n; start += step) { const int count = std::min(step,n-start); std::copy_n(F1.data()+start,count,block.data()); std::fill(block.data()+count,block.data()+length,mint(0)); inplaceDFT(block); pointwise_product(block,block,F2,length,scale); inplaceIDFT(block); for (int i = 0; i < count+m-1; ++i) ans.data()[start+i] += block.data()[i]; } return ans; } const int size = upperpow2(n+m-1); assert(unsigned(size) <= (1u << log_max_length)); F1.f.reserve(size); F2.f.reserve(size); F1.resize(size); F2.resize(size); auto* a = &F1[0]; const auto* b = &F2[0]; // Two Montgomery reductions cancel R^2. Include 1/size here, so the // inverse transform directly returns normalized, ordinary residues. const uint scale = ull(R2)*modpow(size,M-2)%M; #if defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__)) if (use_avx2()) { if (n <= size/2) transform_avx2(a,size); else transform_avx2(a,size); if (m <= size/2) transform_avx2(&F2[0],size); else transform_avx2(&F2[0],size); multiply_avx2(a,a,b,size,scale); transform_avx2(a,size); } else #endif { inplaceDFT(F1); inplaceDFT(F2); for (int i = 0; i < size; ++i) a[i].val = montgomery_mul(montgomery_mul(a[i].val,b[i].val),scale); inplaceIDFT(F1); } F1.resize(n+m-1); return F1; } public: // Copy lvalues directly into the final NTT allocation. Rvalues retain their // storage, so std::move still avoids copying when the caller is finished. fps convolution(const fps& a, const fps& b) const { const int capacity = convolution_capacity(a,b); if (capacity == 0) return convolution_owned(a,b); return convolution_owned(convolution_copy(a,capacity),convolution_copy(b,capacity)); } fps convolution(fps&& a, fps&& b) const { // Moving the same series twice must retain the original square semantics. if (&a == &b) return convolution_owned(a,b); return convolution_owned(std::move(a),std::move(b)); } fps convolution(fps&& a, const fps& b) const { const int capacity = convolution_capacity(a,b); auto copy = capacity ? convolution_copy(b,capacity) : b; return convolution_owned(std::move(a),std::move(copy)); } fps convolution(const fps& a, fps&& b) const { const int capacity = convolution_capacity(a,b); auto copy = capacity ? convolution_copy(a,capacity) : a; return convolution_owned(std::move(copy),std::move(b)); } fps add(const fps& F1, const fps& F2) const { fps ret(max(F1.size(),F2.size())); for (uint i = 0; i < F1.size(); ++i) ret[i] = F1[i]; for (uint i = 0; i < F2.size(); ++i) ret[i] += F2[i]; return ret; } fps subtract(const fps& F1, const fps& F2) const { fps ret(max(F1.size(),F2.size())); for (uint i = 0; i < F1.size(); ++i) ret[i] = F1[i]; for (uint i = 0; i < F2.size(); ++i) ret[i] -= F2[i]; return ret; } /// @brief F*G == 1 mod x^n となるGを求める F[x^0] != 0 が必要 fps inv(const fps& F, const int n) const { assert(n >= 0); if (n == 0) return fps(0); assert(!F.empty() && F[0] != 0); fps res{F[0].inv()}; for (int d = 1; d < n; d <<= 1) { fps f(2*d), g(2*d); for (int j = 0, limit = min(F.size(),2*d); j < limit; ++j) f[j] = F[j]; for (int j = 0; j < d; ++j) g[j] = res[j]; inplaceDFT(f); inplaceDFT(g); const mint iz = mint(2*d).inv(); pointwise_product(f,f,g,2*d,iz); inplaceIDFT(f); for (int j = 0; j < d; ++j) { f[j] = 0; f[j+d] = -f[j+d]; } inplaceDFT(f); pointwise_product(f,f,g,2*d,iz); inplaceIDFT(f); for (int j = 0; j < d; ++j) f[j] = res[j]; res = std::move(f); } res.resize(n); return res; } /// @brief F÷G の商と余りを求める。{商, 余り}として返される。 array,2> div(fps F, fpsG) const { while (!F.f.empty() && F.f.back() == 0){ F.f.pop(); F.sz--; } while (!G.f.empty() && G.f.back() == 0){ G.f.pop(); G.sz--; } int n = F.sz, m = G.sz; if (n < m){return {fps{}, F};} if (min(m, n-m+1) <= 100){ fps res(n-m+1); mint t = G.f.back().inv(); for (int i = n-1; i >= m-1; i--){ auto u = t*F[i]; res[i-m+1] = u; for (int j = 0; j < m; j++){ F[i-j] -= G[m-1-j]*u; } } while (!F.f.empty() && F.f.back() == 0){ F.f.pop(); F.sz--; } while (!G.f.empty() && G.f.back() == 0){ G.f.pop(); G.sz--; } return {res, F}; } auto F2 = F; auto G2 = G; reverse(vall(F.f)); reverse(vall(G.f)); while (len(F.f) > n-m+1){ F.f.pop(); F.sz--; } while (len(G.f) > n-m+1){ G.f.pop(); G.sz--; } auto res = convolution(F, inv(G, n-m+1)); while (len(res.f) > n-m+1){ res.f.pop(); res.sz--; } reverse(vall(res.f)); auto res2 = subtract(F2, convolution(res, G2)); while (!res2.f.empty() && res2.f.back() == 0){ res2.f.pop(); res2.sz--; } return {res, res2}; } /// @brief F を G で割った余りを求める。 fps remainder(fps F, const fps& G) const { while (!F.f.empty() && F.f.back() == 0){ F.f.pop(); F.sz--; } assert(!G.f.empty() && G.f.back() != 0); int n = F.sz, m = G.sz; if (n < m) return F; if (min(m, n-m+1) <= 100){ const bool monic = (G[m-1] == mint(1)); const mint inverse_leading_coefficient = monic ? mint(1) : G[m-1].inv(); for (int i = n-1; i >= m-1; i--){ if (F[i] == 0) continue; mint q = monic ? F[i] : inverse_leading_coefficient*F[i]; for (int j = 0; j < m; j++){ F[i-j] -= G[m-1-j]*q; } } F.resize(m-1); while (!F.f.empty() && F.f.back() == 0){ F.f.pop(); F.sz--; } return F; } auto qr = div(std::move(F), G); return std::move(qr[1]); } /// @brief exp(F) mod x^n を求める F[x^0] == 0 が必要 fps exp(const fps& F, const int n, const mod_table& mtable) const { assert(n >= 0); if (n == 0) return fps(0); assert(F.empty() || F[0] == 0); ull sz_f = F.sz; fps F0{1},f0{1},g0{1}; for(int d = 1; d < n; d<<=1){ auto G0 = g0; inplaceDFT(G0); const mint iz = mint(d).inv(), iz2 = mint(2*d).inv(); fps Delta(d); pointwise_product(Delta,F0,G0,d,iz); inplaceIDFT(Delta); Delta[0] -= 1; fps delta(2*d); for(int j = 0; j < d; j++) delta[d+j] = Delta[j]; fps epsilon(2*d); fps DF0(d-1); for (ull j = 0, ulld = d; j < ulld-1; j++) DF0[j] = f0[j+1]*mint(j+1); DF0.f.append(0); DF0.sz++; inplaceDFT(DF0); pointwise_product(DF0,DF0,G0,d,iz); inplaceIDFT(DF0); for (ull j = 0, limit = d-1; j < limit; ++j) { const auto df = j+1 < sz_f ? F[j+1]*mint(j+1) : mint(0); epsilon[j] = df; epsilon[j+d] = DF0[j]-df; } epsilon[d-1] = DF0[d-1]; Delta = std::move(delta); inplaceDFT(Delta); fps DH0(d-1); for (ull j = 0, limit = d-1; j < limit; j++) DH0[j] = j+1 < sz_f ? F[j+1]*mint(j+1) : mint(0); DH0.resize(2*d); inplaceDFT(DH0); pointwise_product(Delta,Delta,DH0,2*d,iz2); inplaceIDFT(Delta); for(int j = 0; j < d; j++){ epsilon[j+d] -= Delta[j+d]; } for (ull i = 2*d-1; i >= 1; i--){ epsilon[i] = epsilon[i-1]*mtable.inverse_mod(i)-(i < sz_f ? F[i] : mint(0)); } epsilon[0] = 0; auto Epsilon = std::move(epsilon); inplaceDFT(Epsilon); for (int j = 0; j < d; j++){DH0[j] = f0[j]; DH0[j+d].val = 0;} inplaceDFT(DH0); pointwise_product(Epsilon,Epsilon,DH0,2*d,iz2); inplaceIDFT(Epsilon); f0.resize(2*d); for (int j = 0; j < d; j++){ f0[j+d] -= Epsilon[j+d]; } if(2*d >= n) break; G0.resize(2*d); for (int j = 0; j < d; j++) G0[j] = g0[j]; inplaceDFT(G0); F0 = f0; inplaceDFT(F0); fps T(2*d); pointwise_product(T,F0,G0,2*d,iz2); inplaceIDFT(T); for (int j = 0; j < d; j++){ T[j].val = 0; T[j+d] = -T[j+d]; } inplaceDFT(T); pointwise_product(T,T,G0,2*d,iz2); inplaceIDFT(T); for (int j = 0; j < d; j++) T[j] = g0[j]; g0 = std::move(T); } f0.resize(n); return f0; } /// @brief log(F) mod x^n を求める F[x^0] == 1 が必要 fps log(const fps& F, const int n, const mod_table& mtable) const { assert(n >= 0); if (n == 0) return fps(0); assert(!F.empty() && F[0] == 1); const int degree = min(F.size(),n)-1; fps derivative(degree); for (int i = 0; i < degree; ++i) derivative[i] = F[i+1]*mint(i+1); auto res = convolution(std::move(derivative),inv(F,n)); res.resize(n); for (int i = n-1; i >= 1; --i) res[i] = res[i-1]*mtable.inverse_mod(i); res[0] = 0; return res; } /// @brief F^k mod x^n を求める fps pow(const fps& F, const ull k, const int n, const mod_table& mtable) const { assert(n >= 0); if (n == 0) return fps(0); fps ret(n); if (k == 0) { ret[0] = 1; return ret; } int lowest = 0; while (lowest < min(F.size(),n) && F[lowest] == 0) ++lowest; if (lowest == min(F.size(),n)) return ret; // Check before multiplying, including k = ULLONG_MAX. if (lowest > 0 && k > ull((n-1)/lowest)) return ret; const int offset = ull(lowest)*k; const int precision = n-offset; const auto leading = F[lowest], inverse_leading = leading.inv(); fps g(precision); for (int i = 0, limit = min(precision,F.size()-lowest); i < limit; ++i) g[i] = F[lowest+i]*inverse_leading; auto logarithm = log(g,precision,mtable); const mint exponent = k; for (auto& x : logarithm.f) x *= exponent; auto result = exp(logarithm,precision,mtable); const auto scale = leading.pow(k); for (int i = 0; i < precision; ++i) ret[offset+i] = result[i]*scale; return ret; } // f(g(x)) mod x^len(f) を求める fps composition(fps f, fps g, const mod_table& mtable) const { f.resize(max(f.size(),g.size())); int N = f.size(); if (N == 0) return fps(0); int n = 1; while (n < (int)f.size()) n *= 2; f.resize(n), g.resize(n); fps W(2*n); { // bit reverse order list btr(2*n); int log = 31-__builtin_clz(2*n); for (int i = 0; i < 2*n; i++){btr[i] = (btr[i >> 1] >> 1) + ((i & 1) << (log - 1));} int t = log_max_length; uint r = last_powroot; ull dw = modpow(inverse_mod(r, M), (1< &Q) -> fps { if (n == 1) { reverse(f.f.begin(), f.f.end()); fps p(2*k); for (int i = 0; i < k; i++) p[2 * i] = f[i]; return p; } Q.resize(4*n*k); Q[2*n*k].val = 1; inplaceDFT(Q); fps nxt_Q(2*n*k); for (int i = 0; i < 2*n*k; i++) nxt_Q[i] = Q[2*i]*Q[2*i+1]; inplaceIDFT(nxt_Q); ull iz = inverse_mod(nxt_Q.size(), M); for (uint i = 0; i < nxt_Q.size(); i++) nxt_Q[i].val = nxt_Q[i].val*iz%M; for (int j = 0; j < 2*k; j++) for (int i = n/2; i < n; i++) nxt_Q[n*j+i].val = 0; nxt_Q[0].val = 0; fps p = rec(rec, n/2, 2*k, nxt_Q); for (int j = 0; j < 2*k; j++) for (int i = n/2; i < n; i++) p[n * j + i].val = 0; iz = mtable.inverse_mod(p.size()).val; for (uint i = 0; i < p.size(); i++) p[i].val = p[i].val*iz%M; inplaceIDFT_T(p); p.resize(4*n*k); for (int i = 2*n*k-1; i >= 0; i--){ const auto t = mtable.inverse_mod(2)*W[i]*p[i]; p[2*i+1] = -t*Q[2*i]; p[2*i] = t*Q[2*i+1]; } inplaceDFT_T(p); p.resize(2*n*k); return p; }; fps Q(2*n); for(int i = 0; i < n; i++) Q[i].val = g[i].val == 0 ? 0 : M-g[i].val; fps p = rec(rec, n, 1, Q); p.resize(n); reverse(p.f.begin(), p.f.end()); p.resize(N); return p; } fps power_projection(fps f, fps wt, int m, const mod_table& mtable) const { assert(len(f) == len(wt)); if (f.empty()) { return fps(m + 1, 0); } if (f[0] != 0) { auto c = f[0]; f[0] = 0; fps A = power_projection(f, wt, m, mtable); for (int p = 0; p < m + 1; p++) A[p] *= mtable.factorialmodinv(p); fps B(m + 1); mint pow = 1; for (int q = 0; q < m + 1; q++) B[q] = pow * mtable.factorialmodinv(q), pow *= c; A = convolution(A, B); A.resize(m + 1); for (int i = 0; i < m + 1; i++) A[i] *= mtable.factorialmod(i); return A; } int n = 1; while (n < len(f)) n *= 2; for (auto& x: f.f) x = -x; f.resize(n), wt.resize(n); reverse(vall(wt.f)); fps&P = wt, &Q = f; P.resize(4 * n), Q.resize(4 * n); fps W(n); { // bit reverse order list btr(n); int log = 31-__builtin_clz(n); for (int i = 1; i < n; i++) { btr[i] = (btr[i >> 1] >> 1) + ((i & 1) << (log - 1)); } int t = log_max_length; mint r = last_powroot; mint dw = r.inv().pow((1 << t) / (2 * n)); mint w = 1; for (auto i: btr) { W[i] = w, w *= dw; } } int k = 1; while (n > 1) { auto normalized_IDFT = [&](fps& A) { inplaceIDFT(A); mint iz = mint(A.size()).inv(); for (auto& x: A.f) x *= iz; }; auto doubling_y = [&](fps& A, int l, int r) { mint z = W[k / 2].inv(); fps t(k); for (int i = l; i < r; i++) { for (int j = 0; j < k; j++) t[j] = A[2 * n * j + i]; normalized_IDFT(t); mint x = 1; for (int j = 0; j < k; j++) t[j] *= x, x *= z; inplaceDFT(t); for (int j = 0; j < k; j++) A[2 * n * (k + j) + i] = t[j]; } }; auto FFT_x = [&](fps& A, int l, int r) { fps t(2 * n); for (int j = l; j < r; j++) { copy(A.f.begin() + 2 * n * j, A.f.begin() + 2 * n * (j + 1), t.f.begin()); inplaceDFT(t); copy(vall(t.f), A.f.begin() + 2 * n * j); } }; if (n <= k) { doubling_y(P, 0, n), doubling_y(Q, 1, n); FFT_x(P, 0, 2 * k), FFT_x(Q, 0, 2 * k); } else { FFT_x(P, 0, k), FFT_x(Q, 0, k); doubling_y(P, 0, 2 * n), doubling_y(Q, 0, 2 * n); } for (int i = 0; i < 2 * n * k; i++) Q[i] += 1; for (int i = 2 * n * k; i < 4 * n * k; i++) Q[i] -= 1; fps F(2 * n), G(2 * n), p(n), q(n); for (int j = 0; j < 2 * k; j++) { copy(P.f.begin() + 2 * n * j, P.f.begin() + 2 * n * (j + 1), F.f.begin()); copy(Q.f.begin() + 2 * n * j, Q.f.begin() + 2 * n * (j + 1), G.f.begin()); for (int i = 0; i < n; i++) { p[i] = W[i] * (F[2 * i] * G[2 * i + 1] - F[2 * i + 1] * G[2 * i]); q[i] = G[2 * i] * G[2 * i + 1]; } normalized_IDFT(p), normalized_IDFT(q); fill(p.f.begin() + n / 2, p.f.end(), 0); fill(q.f.begin() + n / 2, q.f.end(), 0); copy(vall(p.f), P.f.begin() + n * j); copy(vall(q.f), Q.f.begin() + n * j); } fill(P.f.begin() + 2 * n * k, P.f.end(), 0); fill(Q.f.begin() + 2 * n * k, Q.f.end(), 0); for (int j = 0; j < 4 * k; j++) Q[n * j] = 0; n /= 2, k *= 2; } for (int i = 0; i < k; i++) P[i] = P[2 * i]; P.resize(k); mint ik = mint(k).inv(); for (auto& x: P.f) x *= ik; inplaceIDFT(P); for (auto& x: P.f) x *= ik; reverse(vall(P.f)); P.resize(m + 1); return P; } fps compositional_inverse(fps f, mod_table& mtable) const { /* 仮定:[x^1]f = c は 0 ではない. f を改めて cf と書く. f の逆関数を g とすれば g(cf(x)/c)=x なので g(x/c) が求めるもの.よって [x^1]f=1 に帰着. f, g は n 次とする. Lagrange inversion formula n[x^n]f(x)^i = [x^{n-i}] i(g(x)/x)^{-n} 左辺を i=1,...,n で求めると (g(x)/x)^{-n} が n-1 次まで求まる. [x^1]g=1 に帰着していたので -1/n 乗根がとれて, g(x)/x が n-1 次まで求まる. */ const int n = len(f) - 1; if (n == -1) return {}; assert(f[0] == 0); if (n == 0) return f; assert(f[1] != 0); mint c = f[1]; mint ic = c.inv(); for (auto& x: f.f) x *= ic; fps wt(n + 1); wt[n] = 1; fps A = power_projection(f, wt, n, mtable); fps g(n); for (int i = 1; i < n + 1; i++) g[n - i] = mint(n) * A[i] * mint(i).inv(); g = pow(g, M - mint(n).inv().val, len(g), mtable); g.f.insert(g.f.begin(), 0); g.sz++; mint pow = 1; for (int i = 0; i < len(g); i++) g[i] *= pow, pow *= ic; return g; } // f(x+a)を求める fps polynominal_taylor_shift(const fps& F, ll a, const mod_table& mtable){ const int n = F.size(); const mint shift = a; if (n == 0 || shift == 0) return F; fps g(n), h(n); mint power = 1; for (int i = 0; i < n; ++i) { g[n-1-i] = F[i]*mtable.factorialmod(i); h[i] = power*mtable.factorialmodinv(i); power *= shift; } g = convolution(std::move(g),std::move(h)); fps result(n); for (int i = 0; i < n; ++i) result[i] = g[n-1-i]*mtable.factorialmodinv(i); return result; } // fの微分を求める。 fps differential(const fps& F) const { fps G = F; G.differential(); return G; } // fの積分を求める。積分定数は0 fps integral(const fps& F, const mod_table& mtable) const { fps G = F; G.integral(mtable); return G; } }; /// @brief `[x^N].val(P(x)/Q(x))` をmod Mで求める template constant_modint Bostan_Mori(const ll N, FormalPowerSeries P, FormalPowerSeries Q, const fps_operator& op){ assert(N >= 0); if (N == 0){ return P[0].val*inverse_mod(Q[0].val, M)%M; } FormalPowerSeries Q_minus; const int maxloop = (N == 1 ? 1 : 65-__builtin_clzll(N-1)); for (int _i_ = 0; _i_ < maxloop; _i_++){ Q_minus.resize(Q.size()); for (size_t i = 0; i < Q_minus.size(); i += 2){ Q_minus[i] = Q[i]; } for (size_t i = 1; i < Q_minus.size(); i += 2){ Q_minus[i].val = (M-1)*Q[i].val%M; } auto A = op.convolution(P,Q_minus); auto B = op.convolution(Q,Q_minus); Q.resize((B.size()+1)/2); for (size_t i = 0; i < B.size(); i += 2){ Q[i/2] = B[i]; } P.resize((A.size()+!btest_for_fps(N,_i_))/2); for (size_t i = btest_for_fps(N,_i_); i < A.size(); i += 2){ P[i/2] = A[i]; } } return P[0].val*inverse_mod(Q[0].val, M)%M; } template list> multipoint_product_tree(const list>& samples, const fps_operator& op){ const int n = len(samples); if (n == 0) return {}; list> tree(2*n-1); int next_id = 0; auto build = [&](auto&& self, int l, int r) -> int { const int node = next_id++; if (r-l == 1){ tree[node] = FormalPowerSeries{-samples[l], constant_modint(1)}; return node; } const int mid = (l+r)/2; const int left = self(self, l, mid); const int right = self(self, mid, r); if (r-l == 2){ const auto x0 = samples[l], x1 = samples[l+1]; tree[node] = FormalPowerSeries{x0*x1, -(x0+x1), constant_modint(1)}; } else if (r-l == 3){ const auto x0 = samples[l], x1 = samples[l+1], x2 = samples[l+2]; tree[node] = FormalPowerSeries{ -x0*x1*x2, x0*x1+x0*x2+x1*x2, -(x0+x1+x2), constant_modint(1) }; } else{ tree[node] = op.convolution(tree[left], tree[right]); } return node; }; build(build, 0, n); return tree; } template list> multipoint_evaluation_with_product_tree(const FormalPowerSeries& F, const list>& samples, const list>& tree, const fps_operator& op){ const int n = len(samples); if (n == 0) return {}; list> ans(n); list> remainders; remainders.reserve(n); list> stack; stack.reserve(64); remainders.append(F); stack.append({0,n,0,0}); while (!stack.empty()){ auto [l,r,node,remainder_index] = stack.pop(); if (l >= r){continue;} if (r-l == 1){ constant_modint value = 0; for (int i = int(remainders[remainder_index].sz)-1; i >= 0; i--){ value = value*samples[l]+remainders[remainder_index][i]; } ans[l] = value; continue; } if (remainders[remainder_index].size() >= tree[node].size()){ remainders.append(op.remainder(remainders[remainder_index], tree[node])); remainder_index = int(remainders.size())-1; } const int mid = (l+r)/2; const int left = node+1; const int right = node+2*(mid-l); stack.append({mid,r,right,remainder_index}); stack.append({l,mid,left,remainder_index}); } return ans; } template list> multipoint_evaluation(const FormalPowerSeries& F, const list>& samples, const fps_operator& op){ auto tree = multipoint_product_tree(samples, op); return multipoint_evaluation_with_product_tree(F, samples, tree, op); } template FormalPowerSeries product_of_polynomial_sequence(const list>& A, const fps_operator& op){ delist> dq; dq.append(FormalPowerSeries<998244353>{1}); for (auto& f : A){ dq.append(std::move(f)); } while (dq.size() > 1){ dq.append(op.convolution(std::move(*dq.begin()), std::move(*++dq.begin()))); dq.popleft(); dq.popleft(); } return dq.front(); } template FormalPowerSeries polynomial_interpolation(const list>& X, const list>& Y, const fps_operator& op){ assert(len(X) == len(Y)); const int N = len(X); if (N == 0) return {}; const auto tree = multipoint_product_tree(X, op); auto product_derivative = op.differential(tree[0]); list> Z = multipoint_evaluation_with_product_tree(product_derivative, X, tree, op); list> prefix_product(N+1); prefix_product[0] = 1; for (int i = 0; i < N; i++){ prefix_product[i+1] = prefix_product[i]*Z[i]; } constant_modint inverse_suffix_product = prefix_product[N].inv(); for (int i = N-1; i >= 0; i--){ const auto denominator = Z[i]; Z[i] = Y[i]*prefix_product[i]*inverse_suffix_product; inverse_suffix_product *= denominator; } auto interpolate = [&](auto&& self, int l, int r, int node) -> FormalPowerSeries { if (r-l == 1){ return FormalPowerSeries{Z[l]}; } if (r-l == 2){ const auto x0 = X[l], x1 = X[l+1]; const auto z0 = Z[l], z1 = Z[l+1]; return FormalPowerSeries{-(z0*x1+z1*x0), z0+z1}; } if (r-l == 3){ const auto x0 = X[l], x1 = X[l+1], x2 = X[l+2]; const auto z0 = Z[l], z1 = Z[l+1], z2 = Z[l+2]; return FormalPowerSeries{ z0*x1*x2+z1*x0*x2+z2*x0*x1, -(z0*(x1+x2)+z1*(x0+x2)+z2*(x0+x1)), z0+z1+z2 }; } const int mid = (l+r)/2; const int left = node+1; const int right = node+2*(mid-l); auto left_interpolation = self(self, l, mid, left); auto right_interpolation = self(self, mid, r, right); return op.add( op.convolution(std::move(left_interpolation), tree[right]), op.convolution(tree[left], std::move(right_interpolation)) ); }; return interpolate(interpolate, 0, N, 0); } #undef vall #endif /* FPS_OPERATION_HPP_ */ using mint = constant_modint<998244353>; fps_operator<998244353> op; mod_table<998244353> mtable(1000000); fastio::FastInput in; fastio::FastOutput out; void solve(){ ll N,M,K; in.read(N,M,K); mint nCr = 1; FormalPowerSeries<998244353> F(K+1); for (ll i = 0; i <= min(M, K); i++){ F[i] = nCr*mint(M-i).pow(N); nCr *= mint(M-i)*mtable.inverse_mod(i+1); } // out.print(F); FormalPowerSeries<998244353> G(K+1); G[0] = 0; for (ll i = 1; i <= K; i++){ G[i] = mtable.factorialmodinv(i); if (i%2){G[i] *= -1;} } // out.print(G); F = op.composition(F, G, mtable); for (ll i = 0; i <= K; i++){ G[i] = mint(M).pow(i)*mtable.factorialmodinv(i); } F = op.convolution(F,G); for (ll i = 1; i <= K; i++){ out.print(F[i]*mtable.factorialmod(i)); } } int main(){ ios::sync_with_stdio(false); std::cin.tie(nullptr); ll T = 1; // cin >> T; while (T--){ solve(); } }