結果

問題 No.3770 Sum of Pow of Distinct Count
コンテスト
ユーザー 2251799813685248
提出日時 2026-10-18 13:33:07
言語 C++23
(gcc 15.3.0 + boost 1.92.0 + ACL)
コンパイル:
g++-15 -O2 -lm -std=c++23 -Wuninitialized -DONLINE_JUDGE -o a.out _filename_
実行:
./a.out
結果
AC  
実行時間 425 ms / 4,000 ms
+ 306µs
コード長 127,287 bytes
記録
記録タグの例:
初AC ショートコード 純ショートコード 純主流ショートコード 最速実行時間
コンパイル時間 5,763 ms
コンパイル使用メモリ 424,140 KB
実行使用メモリ 142,704 KB
最終ジャッジ日時 2026-10-18 13:33:19
合計ジャッジ時間 12,038 ms
ジャッジサーバーID
(参考情報)
judge5_0 / judge1_0
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
sample AC * 3
other AC * 15
権限があれば一括ダウンロードができます

ソースコード

diff #
raw source code

// #define _GLIBCXX_DEBUG

#ifndef LIST__HPP_
#define LIST__HPP_

#include <algorithm>
#include <iostream>
#include <cassert>
#include <functional>
#include <utility>
using uint = unsigned int;
using ll = long long;
using namespace std;


template<typename T> concept has_identity = requires (){T();};
template<typename T> concept has_plus_operator = requires (T a, T b){a+b;};
template<typename T> concept has_compare_operator = requires (T a, T b){a < b;};
template<typename T> concept has_equal_operator = requires (T a, T b){a == b; a != b;};


template<typename T>
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<T> 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<ll>(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<T>& 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<T>&& 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 <class InputIterator> 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<T> 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<T> 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<T> 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<T> && has_identity<T>{
        auto res = T();
        for (int i = 0; i < sz_; i++){
            res += A[i];
        }
        return res;
    }
    template<typename U> inline constexpr U sum(U init) const requires has_plus_operator<T>{
        for (int i = 0; i < sz_; i++){
            init += A[i];
        }
        return init;
    }
    inline constexpr void sort(bool reverse = false) requires has_compare_operator<T> {
        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<bool(T,T)>& f) requires has_compare_operator<T> {
        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<T(T)>& f){for (int i = 0; i < sz_; i++){A[i] = f(A[i]);}}
    inline constexpr list<pair<int, T>> RLE(){
        list<pair<int, T>> 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<T> {return std::lower_bound(begin(), end(), val);}
    inline constexpr iterator upper_bound(const T& val) const requires has_compare_operator<T> {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 <iostream>
#include <iterator>
#include <type_traits>
#include <utility>
#include <string>
#include <cmath>
#include <unordered_set>
#include <unordered_map>
#include <queue>
#include <algorithm>
#include <iomanip>
#include <cassert>
#include <functional>
#include <random>
#include <bitset>

// #include <boost/multiprecision/cpp_int.hpp>



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<int,2>;
using pll = array<ll,2>;
using plll = array<lll,2>;
using pld = array<ld, 2>;
using tll = array<ll,3>;

#define vall(A) A.begin(), A.end()
template<typename T> inline bool btest(T K, int i){return K&(1ull<<i);}
#ifndef LEN_DEFINED
#define LEN_DEFINED
template<typename T> 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 <array>
#include <bit>
#include <charconv>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <iterator>
#include <string>
#include <string_view>
#include <tuple>
#include <type_traits>
#include <utility>

// 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<class T> inline constexpr bool integer = std::is_integral_v<T>;
template<class T> struct unsigned_type { using type = std::make_unsigned_t<T>; };
template<> struct unsigned_type<bool> { 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<class T> using unsigned_t = typename unsigned_type<T>::type;
template<class T> inline constexpr bool signed_integer = integer<T> && T(-1) < T(0);
template<class T> concept modular = requires(T x) { x.val; } &&
    (requires { T::mod_value; } || requires { T::mod(); });
template<class T> constexpr auto modulus() {
    if constexpr (requires { T::mod_value; }) return T::mod_value;
    else return T::mod();
}
template<class T> concept indexed_range = requires(T& x) { std::size(x); x[0]; };
template<class T> concept tuple_like = requires { std::tuple_size<T>::value; };
template<class> inline constexpr bool unsupported = false;
inline constexpr auto digit_pairs = [] {
    std::array<char, 200> 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<std::array<char,4>,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<class T> inline constexpr bool output32 = [] {
    if constexpr (integer<T>) return sizeof(T) <= 4 && !std::is_same_v<T,char>;
    else if constexpr (modular<T>) return integer<decltype(T{}.val)> && sizeof(T{}.val) <= 4;
    else return false;
}();
template<class T> inline char* decimal_small(char* p, const T& value) {
    if constexpr (modular<T>) return decimal_small(p,value.val);
    else {
        std::uint32_t u = static_cast<std::uint32_t>(value);
        if constexpr (signed_integer<T>) {
            if (value < 0) { *p++ = '-'; u = 0-u; }
        }
        return decimal32(p,u);
    }
}
} // namespace detail

template<std::size_t BufferSize = 1 << 18>
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<unsigned char>(buffer_[position_]);
    }
    int skip_space() {
        int c;
        while ((c = peek()) != EOF && c <= ' ') ++position_;
        return c;
    }
    template<class U, bool SingleBlock = false> 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<unsigned>(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<std::uint32_t>(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<unsigned>(static_cast<unsigned char>(*p)-'0')) <= 9) {
                value = value*10+digit;
                ++p;
            }
            position_ = static_cast<std::size_t>(p-buffer_);
            if (position_ != size_) break;
            c = peek();
        } while (static_cast<unsigned>(c-'0') <= 9);
        return true;
    }
    template<class T> bool read_one(T& value) {
        if constexpr (std::is_same_v<T, char>) {
            const int c = skip_space();
            if (c == EOF) return false;
            value = static_cast<char>(c);
            ++position_;
            return true;
        } else if constexpr (detail::integer<T>) {
            detail::unsigned_t<T> u;
            bool negative;
            if (!magnitude(u,negative)) return false;
            if constexpr (detail::signed_integer<T>) {
                // Never negate the minimum signed value, even for int128.
                value = negative && u != 0 ? -static_cast<T>(u-1)-1 : static_cast<T>(u);
            } else {
                if (negative || (std::is_same_v<T,bool> && u > 1)) return false;
                value = static_cast<T>(u);
            }
            return true;
        } else if constexpr (detail::modular<T>) {
            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<std::uint64_t,true>(u,negative)) return false;
            const auto mod = detail::modulus<T>();
            if (u >= mod) u %= mod;
            value.val = negative && u != 0 ? mod-u : u;
            return true;
        } else if constexpr (std::is_same_v<T,std::string>) {
            int c = skip_space();
            if (c == EOF) return false;
            value.clear();
            do {
                value.push_back(static_cast<char>(c));
                ++position_;
                c = peek();
            } while (c != EOF && c > ' ');
            return true;
        } else if constexpr (std::is_floating_point_v<T>) {
            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<T>) {
            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<T>) {
            return std::apply([&](auto&... x) { return (read_one(x) && ...); },value);
        } else {
            static_assert(detail::unsupported<T>, "Unsupported fastio input type");
        }
    }
public:
    explicit BasicInput(std::FILE* file = stdin) : file_(file) {}
    BasicInput(const BasicInput&) = delete;
    BasicInput& operator=(const BasicInput&) = delete;

    template<class... T> bool read(T&... values) {
        if (!good_) return false;
        return good_ = (read_one(values) && ...);
    }
    template<class T> BasicInput& operator>>(T& value) { read(value); return *this; }
    explicit operator bool() const { return good_; }
};

template<std::size_t BufferSize = 1 << 18>
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<class U> 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<std::size_t>(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<unsigned>(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<unsigned>(value));
        else {
            p -= 2;
            std::memcpy(p,detail::digit_pairs.data()+2*static_cast<unsigned>(value),2);
        }
        if (negative) *--p = '-';
        if constexpr (BufferSize >= sizeof(digits)) {
            if (!good_ || (BufferSize-size_ < sizeof(digits) && !drain())) return false;
            auto count = static_cast<std::size_t>(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<std::size_t>(end-p));
    }
    template<class T> bool write_one(const T& value) {
        if constexpr (std::is_same_v<T,char>) {
            return put(value);
        } else if constexpr (detail::integer<T>) {
            using U = detail::unsigned_t<T>;
            U u = static_cast<U>(value);
            bool negative = false;
            if constexpr (detail::signed_integer<T>) {
                negative = value < 0;
                if (negative) u = U(0)-u;
            }
            return unsigned_number(u,negative);
        } else if constexpr (detail::modular<T>) {
            return write_one(value.val);
        } else if constexpr (std::is_convertible_v<const T&,std::string_view>) {
            const std::string_view text(value);
            return bytes(text.data(),text.size());
        } else if constexpr (std::is_floating_point_v<T>) {
            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<std::size_t>(result.ptr-text));
        } else if constexpr (detail::indexed_range<const T>) {
            const auto n = std::size(value);
            using Element = std::remove_cvref_t<decltype(value[0])>;
            if constexpr (detail::indexed_range<Element> || detail::tuple_like<Element>){
                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<Element> && 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<std::size_t>(p-buffer_);
                        if (!drain()) return false;
                        p = buffer_;
                    }
                    if (i != 0) *p++ = ' ';
                    p = detail::decimal_small(p,data[i]);
                }
                size_ = static_cast<std::size_t>(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<T>) {
            [[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<T>, "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<class... T> bool write(const T&... values) {
        if (!good_) return false;
        return good_ = (write_one(values) && ...);
    }
    template<class... T> 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<class... T> 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<class T> 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<class... T> bool read(T&... values) { return in.read(values...); }
// template<class... T> bool write(const T&... values) { return out.write(values...); }
// template<class... T> bool print(const T&... values) { return out.print(values...); }
// template<class... T> 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 <array>

#include <algorithm>
#include <functional>
#if defined(__x86_64__) || defined(__i386__)
#include <immintrin.h>
#endif
#ifndef DEQUE__HPP_
#define DEQUE__HPP_

#include <deque>
#include <iostream>
#include <iterator>
using namespace std;
using ll = long long;
using uint = unsigned int;

template<typename T>
class delist{
    public:

    inline constexpr delist(){A = deque<T>();}
    inline constexpr delist(uint sz, const T& val = T()){A = deque<T>(sz, val);}
    template <class InputIterator> inline constexpr delist(InputIterator first, InputIterator last){A = deque<T>(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<T, T&, T*> begin(){return A.begin();}
    inline constexpr _Deque_iterator<T, T&, T*> begin() const {return A.begin();}
    inline constexpr _Deque_iterator<T, T&, T*> end(){return A.end();}
    inline constexpr _Deque_iterator<T, T&, T*> end() const {return A.end();}


    inline constexpr ll size(){return A.size();}
    inline constexpr bool empty(){return A.empty();}

    private:


    deque<T> A;
};

#endif /* DEQUE__HPP_ */
#ifndef FPS__HPP_
#define FPS__HPP_

#include <iostream>
#include <iterator>

#include <cassert>
#ifndef MODINT__HPP_
#define MODINT__HPP_

#include <iostream>
#ifndef MATH_FUNCTION_HPP_
#define MATH_FUNCTION_HPP_

#include <array>
#include <cmath>
#include <cassert>
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<ll m> 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<typename T> 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<typename T> 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<typename T> 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<typename T>
constexpr array<T,3> 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<typename T, typename U>
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<ll M>
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<M>(a, z) != 1){return -1;}
    int b = 0;
    while (modpow<M>((b*b+M-a)%M, z) == 1){
        b++;
    }
    array<ll,2> x{1,0};
    array<ll,2> 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<ll,2> x{1,0};
    array<ll,2> 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 <uint M>
struct constant_modint {
    static constexpr uint mod_value = M;
    uint val;
    constant_modint() : val(0) {}
    template <class T> 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<ll,ll>(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 <int id = -1>
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 <class T> 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 <cassert>

using uint = unsigned;
using ull = unsigned long long;
using ll = long long;
using namespace std;

/// @brief mod M上での階乗,逆元テーブルを保持する構造体
/// @tparam M 
template<uint M>
class mod_table{
    using mint = constant_modint<M>;
    list<mint> invmodlist;
    list<mint> factorialmodlist;
    list<mint> factorialmodinvlist;
    uint N_MAX;

    public:
    constexpr mod_table(const uint N_MAX__){
        N_MAX = max(1u, N_MAX__);
        invmodlist = list<mint>(N_MAX+1);
        factorialmodlist = list<mint>(N_MAX+1);
        factorialmodinvlist = list<mint>(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<typename T> inline constexpr ll len(const T& A){return ssize(A);}
#endif


template<uint T>
struct FormalPowerSeries{
    using mint = constant_modint<T>;
    list<mint> 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<typename U> constexpr FormalPowerSeries(const list<U>& _init){
        f.assign(_init.begin(), _init.end());
        sz = f.size();
    }
    template<typename U> inline constexpr FormalPowerSeries(initializer_list<U> _init) : FormalPowerSeries(list<U>(_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<typename U> constexpr operator list<U>() const {
        list<U> 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<T>& 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 <array>


#include <algorithm>
#include <cassert>
#include <numeric>
#include <iostream>
using namespace std;
using ll = long long;
using ulll = __uint128_t;
using pll = array<ll,2>;
using pull = array<ull,2>;
using pii = array<int,2>;



/// @brief 試し割り法で正の整数Nを素因数分解する
/// @return list<array<ll,2>>{{素因数1,個数}, {素因数2,個数}, {素因数3,個数}...}
template<typename T> constexpr list<array<T,2>> trial_division(T N){
    assert(N > 0);
    if (N == 1){
        return list<array<T,2>> {{1,0}};
    }
    list<array<T,2>> R;//戻り値用リスト

    const T M = isqrt(static_cast<ull>(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<typename T> constexpr T divisor_function(const list<array<T,2>>& 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<typename T> constexpr list<T> enumerate_divisor(const list<array<T,2>>& p){
    list<T> 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<int> p_list;
    list<int> 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<pii> factorize(int x){
        if (x == 1){return {{1,0}};}
        list<pii> 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<list<pii>> factorize_all(int N){
        list<list<pii>> r(N+1);
        r[1].append({1,0});
        for (int i = 2; i <= N; i++){
            r[i] = factorize(i);
        }
        return r;
    }
    /// @brief N以下の整数に対するメビウス関数の値を列挙する。
    list<int> enumerate_mobius(int N){
        list<int> 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<typename T> 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<T>(a))) return false;
        }
    }
    else if (N < 1000000000000000000){
        for (auto a : __MillerRabin_large){
            if (!check(static_cast<T>(a))) return false;
        }
    }
    else{
        for (auto a : __MillerRabin_large){
            if (!check(static_cast<T>(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<typename T> constexpr list<array<T, 2>> Pollard_rho(T N){
    assert(N > 0);
    if (N == 1){return {{1,0}};}
    list<T> res;
    list<T> dq;
    dq.append(N);
    while (!dq.empty()){
        T n = dq.back();
        dq.pop();
        bool next_loop = false;
        while (!MillerRabin<T>(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<array<T,2>> 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<pull> Pollard_rho(ull N){
    assert(N > 0);
    if (N == 1){return {{1,0}};}
    list<ull> res;
    list<ull> dq;
    dq.append(N);
    while (!dq.empty()){
        ull n = dq.back();
        dq.pop();
        bool next_loop = false;
        while (!MillerRabin<ull>(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<int>(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<pull> 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<ull>(r-l)});
        l = r;
    }
    return res2;
}


/// @brief 素因数分解を行う。`N`の大きさによって、試し割り法とポラード・ロー法を使い分けてくれる。
template<typename T> constexpr list<array<T, 2>> factorize(T N){
    if (N < 640000){
        return trial_division<T>(N);
    }
    if (N < 1000000000000000000){
        return Pollard_rho<T>(N);
    }
    return Pollard_rho<T>(N);
}
template<> constexpr list<pull> factorize(ull N){
    if (N < 640000ull){
        return trial_division<ull>(N);
    }
    return Pollard_rho<ull>(N);
}
template<> constexpr list<pll> factorize(ll N){
    auto res = factorize<ull>(N);
    list<pll> 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<pii> factorize(int N){
    auto res = factorize<ull>(N);
    list<pii> 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<ull>(P));
    if (P == 2){
        return 1;
    }
    Montgomery64 mg(P);
    Xorshift64 rng(0x1234567E89ABCDEF);
    auto factorized = factorize<ull>(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<typename T> inline bool btest_for_fps(T K, int i){return K&(1ull<<i);}


/// @brief mod M上での形式的冪級数の計算を行う構造体
template<uint M>
struct fps_operator{
    template<uint T> using fps = FormalPowerSeries<T>;
    template<uint T> using mint = constant_modint<T>;
    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<ll> powroot{1};
        list<ll> powrootinv;
        while (powroot.back() >= 0){
            powroot.append(cipolla<M>(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<ll,ll>(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<bool InverseButterfly, bool InverseRoots>
    void transform_scalar(mint<M>* 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<bool InverseButterfly, bool InverseRoots, bool Canonical = true, bool ZeroUpperHalf = false>
    __attribute__((target("avx2")))
    void transform_avx2(mint<M>* 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<bool Canonical = true>
    __attribute__((target("avx2")))
    static void multiply_avx2(mint<M>* dst, const mint<M>* a, const mint<M>* 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<M>& dst, const fps<M>& lhs, const fps<M>& rhs,
                           int n, mint<M> 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<bool InverseButterfly, bool InverseRoots>
    void transform(fps<M>& 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<InverseButterfly, InverseRoots>(a,n);
            return;
        }
#endif
        transform_scalar<InverseButterfly, InverseRoots>(a,n);
    }
public:
    void inplaceDFT(fps<M>& F) const { transform<false,false>(F); }
    void inplaceIDFT(fps<M>& F) const { transform<true,true>(F); }
    void inplaceDFT_T(fps<M>& F) const { transform<true,false>(F); }
    void inplaceIDFT_T(fps<M>& F) const { transform<false,true>(F); }

private:
    static fps<M> convolution_copy(const fps<M>& src, int capacity) {
        assert(capacity > 0 && src.size() <= unsigned(capacity));
        fps<M> 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<M>& a, const fps<M>& 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<M> convolution_owned(fps<M> F1, fps<M> F2) const {
        int n = F1.size(), m = F2.size();
        if (n == 0 || m == 0) return fps<M>(0);
        if (std::min(n,m) <= 60) {
            if (n < m) { std::swap(n,m); std::swap(F1,F2); }
            fps<M> 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<M> block(length), ans(n+m-1);
            const mint<M> scale = mint<M>(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<M>(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<M>(size,M-2)%M;
#if defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__))
        if (use_avx2()) {
            if (n <= size/2) transform_avx2<false,false,false,true>(a,size);
            else transform_avx2<false,false,false>(a,size);
            if (m <= size/2) transform_avx2<false,false,false,true>(&F2[0],size);
            else transform_avx2<false,false,false>(&F2[0],size);
            multiply_avx2<false>(a,a,b,size,scale);
            transform_avx2<true,true>(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<M> convolution(const fps<M>& a, const fps<M>& 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<M> convolution(fps<M>&& a, fps<M>&& 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<M> convolution(fps<M>&& a, const fps<M>& 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<M> convolution(const fps<M>& a, fps<M>&& 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<M> add(const fps<M>& F1, const fps<M>& F2) const {
        fps<M> 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<M> subtract(const fps<M>& F1, const fps<M>& F2) const {
        fps<M> 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<M> inv(const fps<M>& F, const int n) const {
        assert(n >= 0);
        if (n == 0) return fps<M>(0);
        assert(!F.empty() && F[0] != 0);
        fps<M> res{F[0].inv()};
        for (int d = 1; d < n; d <<= 1) {
            fps<M> f(2*d), g(2*d);
            for (int j = 0, limit = min<int>(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<M> iz = mint<M>(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<fps<M>,2> div(fps<M> F, fps<M>G) 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<M>{}, F};}

        if (min(m, n-m+1) <= 100){
            fps<M> res(n-m+1);
            mint<M> 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<M> remainder(fps<M> F, const fps<M>& 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<M>(1));
            const mint<M> inverse_leading_coefficient = monic ? mint<M>(1) : G[m-1].inv();
            for (int i = n-1; i >= m-1; i--){
                if (F[i] == 0) continue;
                mint<M> 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<M> exp(const fps<M>& F, const int n, const mod_table<M>& mtable) const {
        assert(n >= 0);
        if (n == 0) return fps<M>(0);
        assert(F.empty() || F[0] == 0);
        ull sz_f = F.sz;
        fps<M> F0{1},f0{1},g0{1};
        for(int d = 1; d < n; d<<=1){
            auto G0 = g0;
            inplaceDFT(G0);
            const mint<M> iz = mint<M>(d).inv(), iz2 = mint<M>(2*d).inv();
            fps<M> Delta(d);
            pointwise_product(Delta,F0,G0,d,iz);
            inplaceIDFT(Delta);
            Delta[0] -= 1;
            fps<M> delta(2*d);
            for(int j = 0; j < d; j++) delta[d+j] = Delta[j];
            fps<M> epsilon(2*d);

            fps<M> DF0(d-1);
            for (ull j = 0, ulld = d; j < ulld-1; j++) DF0[j] = f0[j+1]*mint<M>(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<M>(j+1) : mint<M>(0);
                epsilon[j] = df;
                epsilon[j+d] = DF0[j]-df;
            }
            epsilon[d-1] = DF0[d-1];
            Delta = std::move(delta);
            inplaceDFT(Delta);
            fps<M> DH0(d-1);
            for (ull j = 0, limit = d-1; j < limit; j++) DH0[j] = j+1 < sz_f ? F[j+1]*mint<M>(j+1) : mint<M>(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<M>(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<M> 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<M> log(const fps<M>& F, const int n, const mod_table<M>& mtable) const {
        assert(n >= 0);
        if (n == 0) return fps<M>(0);
        assert(!F.empty() && F[0] == 1);
        const int degree = min<int>(F.size(),n)-1;
        fps<M> derivative(degree);
        for (int i = 0; i < degree; ++i) derivative[i] = F[i+1]*mint<M>(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<M> pow(const fps<M>& F, const ull k, const int n, const mod_table<M>& mtable) const {
        assert(n >= 0);
        if (n == 0) return fps<M>(0);
        fps<M> ret(n);
        if (k == 0) { ret[0] = 1; return ret; }
        int lowest = 0;
        while (lowest < min<int>(F.size(),n) && F[lowest] == 0) ++lowest;
        if (lowest == min<int>(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<M> g(precision);
        for (int i = 0, limit = min<int>(precision,F.size()-lowest); i < limit; ++i)
            g[i] = F[lowest+i]*inverse_leading;
        auto logarithm = log(g,precision,mtable);
        const mint<M> 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<M> composition(fps<M> f, fps<M> g, const mod_table<M>& mtable) const {
        f.resize(max(f.size(),g.size()));
        int N = f.size();
        if (N == 0) return fps<M>(0);

        int n = 1;
        while (n < (int)f.size()) n *= 2;
        f.resize(n), g.resize(n);

        fps<M> W(2*n);
        {
            // bit reverse order
            list<int> 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<M>(inverse_mod<ll,ll>(r, M), (1<<t)/(4*n));
            uint w = 1;
            for (auto i: btr) { W[i].val = w, w = w*dw%M; }
        }

        auto rec = [&](auto &rec, int n, int k, fps<M> &Q) -> fps<M> {
            if (n == 1) {
                reverse(f.f.begin(), f.f.end());
                fps<M> 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<M> 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<ll,ll>(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<M> 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<M> Q(2*n);
        for(int i = 0; i < n; i++) Q[i].val = g[i].val == 0 ? 0 : M-g[i].val;
        fps<M> p = rec(rec, n, 1, Q);
        p.resize(n);
        reverse(p.f.begin(), p.f.end());
        p.resize(N);
        return p;
    }
    fps<M> power_projection(fps<M> f, fps<M> wt, int m, const mod_table<M>& mtable) const {
        assert(len(f) == len(wt));
        if (f.empty()) { return fps<M>(m + 1, 0); }
        if (f[0] != 0) {
            auto c = f[0];
            f[0] = 0;
            fps<M> A = power_projection(f, wt, m, mtable);
            for (int p = 0; p < m + 1; p++) A[p] *= mtable.factorialmodinv(p);
            fps<M> B(m + 1);
            mint<M> 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<M>&P = wt, &Q = f;
        P.resize(4 * n), Q.resize(4 * n);

        fps<M> W(n);
        {
            // bit reverse order
            list<int> 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<M> r = last_powroot;
            mint<M> dw = r.inv().pow((1 << t) / (2 * n));
            mint<M> w = 1;
            for (auto i: btr) { W[i] = w, w *= dw; }
        }

        int k = 1;
        while (n > 1) {
            auto normalized_IDFT = [&](fps<M>& A) {
                inplaceIDFT(A);
                mint<M> iz = mint<M>(A.size()).inv();
                for (auto& x: A.f) x *= iz;
            };
            auto doubling_y = [&](fps<M>& A, int l, int r) {
                mint<M> z = W[k / 2].inv();
                fps<M> 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<M> 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<M>& A, int l, int r) {
                fps<M> 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<M> 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<M> ik = mint<M>(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<M> compositional_inverse(fps<M> f, mod_table<M>& 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<M> c = f[1];
        mint<M> ic = c.inv();
        for (auto& x: f.f) x *= ic;
        fps<M> wt(n + 1);
        wt[n] = 1;

        fps<M> A = power_projection(f, wt, n, mtable);
        fps<M> g(n);
        for (int i = 1; i < n + 1; i++) g[n - i] = mint<M>(n) * A[i] * mint<M>(i).inv();
        g = pow(g, M - mint<M>(n).inv().val, len(g), mtable);
        g.f.insert(g.f.begin(), 0);
        g.sz++;

        mint<M> pow = 1;
        for (int i = 0; i < len(g); i++) g[i] *= pow, pow *= ic;
        return g;
    }
    // f(x+a)を求める
    fps<M> polynominal_taylor_shift(const fps<M>& F, ll a, const mod_table<M>& mtable){
        const int n = F.size();
        const mint<M> shift = a;
        if (n == 0 || shift == 0) return F;
        fps<M> g(n), h(n);
        mint<M> 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<M> result(n);
        for (int i = 0; i < n; ++i) result[i] = g[n-1-i]*mtable.factorialmodinv(i);
        return result;
    }
    // fの微分を求める。
    fps<M> differential(const fps<M>& F) const {
        fps<M> G = F;
        G.differential();
        return G;
    }
    // fの積分を求める。積分定数は0
    fps<M> integral(const fps<M>& F, const mod_table<M>& mtable) const {
        fps<M> G = F;
        G.integral(mtable);
        return G;
    }
};



/// @brief `[x^N].val(P(x)/Q(x))` をmod Mで求める
template<uint M>
constant_modint<M> Bostan_Mori(const ll N, FormalPowerSeries<M> P, FormalPowerSeries<M> Q, const fps_operator<M>& op){
    assert(N >= 0);
    if (N == 0){
        return P[0].val*inverse_mod<ll,ll>(Q[0].val, M)%M;
    }
    FormalPowerSeries<M> 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<ll,ll>(Q[0].val, M)%M;
}

template<uint M>
list<FormalPowerSeries<M>> multipoint_product_tree(const list<constant_modint<M>>& samples, const fps_operator<M>& op){
    const int n = len(samples);
    if (n == 0) return {};

    list<FormalPowerSeries<M>> 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<M>{-samples[l], constant_modint<M>(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<M>{x0*x1, -(x0+x1), constant_modint<M>(1)};
        }
        else if (r-l == 3){
            const auto x0 = samples[l], x1 = samples[l+1], x2 = samples[l+2];
            tree[node] = FormalPowerSeries<M>{
                -x0*x1*x2,
                x0*x1+x0*x2+x1*x2,
                -(x0+x1+x2),
                constant_modint<M>(1)
            };
        }
        else{
            tree[node] = op.convolution(tree[left], tree[right]);
        }
        return node;
    };
    build(build, 0, n);
    return tree;
}

template<uint M>
list<constant_modint<M>> multipoint_evaluation_with_product_tree(const FormalPowerSeries<M>& F, const list<constant_modint<M>>& samples, const list<FormalPowerSeries<M>>& tree, const fps_operator<M>& op){
    const int n = len(samples);
    if (n == 0) return {};

    list<constant_modint<M>> ans(n);
    list<FormalPowerSeries<M>> remainders;
    remainders.reserve(n);
    list<array<int, 4>> 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<M> 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 <uint M>
list<constant_modint<M>> multipoint_evaluation(const FormalPowerSeries<M>& F, const list<constant_modint<M>>& samples, const fps_operator<M>& op){
    auto tree = multipoint_product_tree(samples, op);
    return multipoint_evaluation_with_product_tree(F, samples, tree, op);
}

template<uint M>
FormalPowerSeries<M> product_of_polynomial_sequence(const list<FormalPowerSeries<M>>& A, const fps_operator<M>& op){
    delist<FormalPowerSeries<M>> 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<uint M>
FormalPowerSeries<M> polynomial_interpolation(const list<constant_modint<M>>& X, const list<constant_modint<M>>& Y, const fps_operator<M>& 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<constant_modint<M>> Z =
        multipoint_evaluation_with_product_tree(product_derivative, X, tree, op);

    list<constant_modint<M>> 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<M> 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<M> {
        if (r-l == 1){
            return FormalPowerSeries<M>{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<M>{-(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<M>{
                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();
    }
}



0