#include using namespace std; //多倍長整数. template //mod<2^30. struct modint{ //mod変更が不可能. public: long long v; static void setmod(int m){} //飾り. static constexpr long long getmod(){return mod;} modint():v(0){} template modint(T a):v(a){if(v < 0) v += mod;} long long val()const{return v;} modint &operator=(const modint &b) = default; modint &operator+()const{return (*this);} modint operator-()const{return modint(0)-(*this);} modint operator+(const modint b)const{return modint(v)+=b;} modint operator-(const modint b)const{return modint(v)-=b;} modint operator*(const modint b)const{return modint(v)*=b;} modint operator/(const modint b)const{return modint(v)/=b;} modint &operator+=(const modint b){ v += b.v; if(v >= mod) v -= mod; return *this; } modint &operator-=(const modint b){ v -= b.v; if(v < 0) v += mod; return *this; } modint &operator*=(const modint b){v = v*b.v%mod; return *this;} modint &operator/=(modint b){ //b!=0 mod素数が必須. assert(b.v != 0); (*this) *= b.pow(mod-2); return *this; } modint pow(long long n)const{ modint ret = 1,p = v; if(n < 0) p = p.inv(),n = -n; while(n){ if(n&1) ret *= p; p *= p; n >>= 1; } return ret; } modint inv()const{return pow(mod-2);} //素数mod必須. modint &operator++(){*this += 1; return *this;} modint &operator--(){*this -= 1; return *this;} modint operator++(int){modint ret = *this; *this += 1; return ret;} modint operator--(int){modint ret = *this; *this -= 1; return ret;} friend bool operator==(const modint a,const modint b){return a.v==b.v;} friend bool operator!=(const modint a,const modint b){return a.v!=b.v;} friend bool operator<(const modint a,const modint b){return a.v=(const modint a,const modint b){return a.v>=b.v;} friend bool operator>(const modint a,const modint b){return a.v>b.v;} friend ostream &operator<<(ostream &os,const modint a){return os<>(istream &is,modint &a){ //入力はmodをとってくれる. long long x; is >> x; x %= mod; a = modint(x); return is; } }; using mint = modint<998244353>; const long long mod = 998244353; namespace to_fold{ __int128_t safemod(__int128_t a,long long m){a %= m; if(a < 0) a += m; return a;} pair invgcd(long long a,long long b){ //return {gcd(a,b),x} (xa≡g(mod b)) a = safemod(a,b); if(a == 0) return {b,0}; long long x = 0,y = 1,memob = b; while(a){ long long q = b/a; b -= a*q; swap(x,y); y -= q*x; swap(a,b); } if(x < 0) x += memob/b; return {b,x}; } template long long Garner(const vector &A,const vector &M){ __int128_t mulM = 1,x = A.at(0)%M.at(0); //Mの要素のペア互いに素必須. for(int i=1; i struct fftinfo{ static bool First; static mint g,sum_e[30],sum_ie[30]; //sum_e[i]=Π[j=0~i-1]ies[j] * es[i],sum_ie[i]=Π[i=0~j-1]es[j] * ies[i]. static mint divpow2[30]; //div[i] = 1/(2^i). static mint Zeta[30]; fftinfo(){ if(!First) return; First = false; const long long mod = mint::getmod(); if(mod == 998244353) g = 3; else if(mod == 754974721) g = 11; else if(mod == 167772161) g = 3; else if(mod == 469762049) g = 3; else assert(false); //現状RE. mint es[30],ies[30]; //es[i]^(2^(2+i))=1. int cnt2 = countzero(mod-1); mint e = g.pow((mod-1)>>cnt2),ie = e.inv(); for(int i=cnt2; i>=2; i--){ //e^(2^i)=1; es[i-2] = e,e *= e; ies[i-2] = ie,ie *= ie; } mint rot = 1; for(int i=0; i<=cnt2-2; i++) sum_e[i] = es[i]*rot,rot *= ies[i]; rot = 1; for(int i=0; i<=cnt2-2; i++) sum_ie[i] = ies[i]*rot,rot *= es[i]; mint div2n = 1,div2 = mint(1)/2; for(int i=0; i<30; i++) divpow2[i] = div2n,div2n *= div2; for(int i=0; i<=cnt2; i++) Zeta[i] = g.pow((mod-1)/(2< bool fftinfo::First=true; template mint fftinfo::g; template mint fftinfo::sum_e[30]; template mint fftinfo::sum_ie[30]; template mint fftinfo::divpow2[30]; template mint fftinfo::Zeta[30]; template void NTT(vector &A){ //ACLを超参考にしてる. int n = A.size(); assert((n&-n) == n); fftinfo info; int h = countzero(n); for(int ph=1; ph<=h; ph++){ int w = 1<<(ph-1),p = 1<<(h-ph); mint rot = 1; for(int s=0; s void INTT(vector &A){ int n = A.size(); assert((n&-n) == n); fftinfo info; const unsigned int mod = mint::getmod(); int h = countzero(n); for(int ph=h; ph>0; ph--){ int w = 1<<(ph-1),p = 1<<(h-ph); mint irot = 1; for(int s=0; s vector convolution(vector A,vector B){ //mintじゃないのを突っ込まないように!!!. int siza = A.size(),sizb = B.size(),sizc = siza+sizb-1,N = 1; if(siza == 0 || sizb == 0) return {}; if(min(siza,sizb) <= 60){ //naive. vector ret(sizc); if(siza >= sizb){for(int i=0; i vector convolution_ll(const vector &A,const vector &B){ //long longに収まる範囲. int siza = A.size(),sizb = B.size(),sizc = siza+sizb-1; if(siza == 0 || sizb == 0) return {}; vector ret(sizc); if(min(siza,sizb) <= 200){ //naive 200はやばい?. if(siza >= sizb){for(int i=0; i; using mint2 = modint; using mint3 = modint; vector a1(siza),b1(sizb); vector a2(siza),b2(sizb); vector a3(siza),b3(sizb); for(int i=0; i C1 = convolution(a1,b1); for(int i=0; i C2 = convolution(a2,b2); for(int i=0; i C3 = convolution(a3,b3); vector offset = {0,0,m1m2m3,2*m1m2m3,3*m1m2m3}; for(int i=0; i vector convolution_llmod(const vector &A,const vector &B){ int siza = A.size(),sizb = B.size(),sizc = siza+sizb-1; if(siza == 0 || sizb == 0) return {}; vector ret(sizc); if(min(siza,sizb) <= 200){ for(int i=0; i; using mint2 = modint; using mint3 = modint; vector a1(siza),b1(sizb); vector a2(siza),b2(sizb); vector a3(siza),b3(sizb); for(int i=0; i C1 = convolution(a1,b1); for(int i=0; i C2 = convolution(a2,b2); for(int i=0; i C3 = convolution(a3,b3); for(int i=0; i A = {C1.at(i).v,C2.at(i).v,C3.at(i).v}; vector M = {mod1,mod2,mod3}; ret.at(i) = Garner(A,M); } return ret; } template vector<__int128_t> convolution_i128(const vector &A,const vector &B){ //10^25に収まる範囲. int siza = A.size(),sizb = B.size(),sizc = siza+sizb-1; if(siza == 0 || sizb == 0) return {}; vector<__int128_t> ret(sizc); if(min(siza,sizb) <= 200){ //naive 200はやばい?. if(siza >= sizb){for(int i=0; i; using mint2 = modint; using mint3 = modint; vector a1(siza),b1(sizb); vector a2(siza),b2(sizb); vector a3(siza),b3(sizb); for(int i=0; i C1 = convolution(a1,b1); for(int i=0; i C2 = convolution(a2,b2); for(int i=0; i C3 = convolution(a3,b3); for(int i=0; i convolution_int(const vector &A,const vector &B){ //intに収まる範囲. if(A.size() == 0 || B.size() == 0) return {}; vector ret; if(min(A.size(),B.size()) <= 60){ ret.resize(A.size()+B.size()-1); for(int i=0; i; vector X(A.size()),Y(B.size()),Z; for(int i=0; i void NTTdoubling(vector &A){ //NTTの原理を忘れているため何やってるのか意味が分からない NTT-friendly専用. //INTT->resize(2倍)->NTTの代わりにcopy->INTT->謎の操作->NTT->push sizeが小さい時は効率悪いらしいよ. int n = A.size(); fftinfo info; vector B = A; INTT(B); mint rot = 1,zeta = info.Zeta[countzero(n)]; for(auto &v : B) v *= rot,rot *= zeta; NTT(B); A.reserve(n<<1); for(auto &v : B) A.push_back(v); } bool isNTTfriendly(long long mod){ if(mod == 998244353 || mod == 754974721 || mod == 16777216 || mod == 469762049) return true; return false; //現状false 原子根求める機能を追加してから. int have2 = countzero(mod-1); return have2 >= 20;//とりあえず2^20でokとする; } } using namespace to_fold; struct bigint{ bool isMinus; //0未満か vector V; //絶対値の値 5108->{8,0,1,5} 下位ほど前の要素へ入る(9桁ずつ) static constexpr int D = 1'000'000'000,log = 9; bigint():isMinus(false),V(){} template bigint(T a):isMinus(false){ if(a < 0) isMinus = true,a = -a; while(a) V.push_back(a%D),a /= D; } bigint(bool m,const vector &v):isMinus(m),V(v){} bigint(const string &s):isMinus(false){ assert(s.size()); if(s.at(0) == '-'){ isMinus = true; for(int i=(int)s.size(); i>0; i-=log){ int x = 0; for(int k=max(1,i-log); k=0; i-=log){ int x = 0; for(int k=max(0,i-log); k>(istream &is,bigint &b){string s; is >> s,b = s; return is;} friend ostream &operator<<(ostream &os,const bigint &b){return os< v = sub(r.V,l.V); bool m = r.isMinus; if(iszero(v)) m = false; return {m,v}; } else{ //l>r abs vector v = sub(l.V,r.V); bool m = l.isMinus; return {m,v}; } } friend bigint operator-(const bigint &l,const bigint &r){ //l-r O(n). if(l.isMinus != r.isMinus) return {l.isMinus,add(l.V,r.V)}; if(isleEq(l.V,r.V)){ //l<=r abs vector v = sub(r.V,l.V); bool m = r.isMinus^1; if(iszero(v)) m = false; return {m,v}; } else{ //l>r abs vector v = sub(l.V,r.V); bool m = l.isMinus; return {m,v}; } } friend bigint operator*(const bigint &l,const bigint &r){ //l*r O(nlogn or n^2). vector v = mul(l.V,r.V); bool m = l.isMinus^r.isMinus; if(iszero(v)) m = false; return {m,v}; } friend bigint operator/(const bigint &l,const bigint &r){return getQR(l,r).first;} //l/r O(nlogn or n^2). friend bigint operator%(const bigint &l,const bigint &r){return getQR(l,r).second;} //l%r O(nlogn or n^2). friend pair getQR(const bigint &l,const bigint &r){ //{l/r,l%r} O(nlogn or n^2). auto qr = div(l.V,r.V); bool mq = l.isMinus!=r.isMinus,mr = l.isMinus; if(iszero(qr.first)) mq = false; if(iszero(qr.second)) mr = false; return {{mq,qr.first},{mr,qr.second}}; } friend bool operator==(const bigint &l,const bigint &r){ if(l.isMinus != r.isMinus || l.size() != r.size()) return false; return l.V == r.V; } friend bool operator!=(const bigint &l,const bigint &r){ if(l.isMinus != r.isMinus || l.size() != r.size()) return true; return l.V != r.V; } friend bool operator<(const bigint &l,const bigint &r){return isless(l,r);} friend bool operator<=(const bigint &l,const bigint &r){return isleEq(l,r);} friend bool operator>=(const bigint &l,const bigint &r){return isleEq(r,l);} friend bool operator>(const bigint &l,const bigint &r){return isless(r,l);} bool iszero()const{return !V.size();} //(*this)==0? string to_string()const{ //文字列へ. if(iszero()) return "0"; string ret = ""; if(isMinus) ret += '-'; for(int i=(int)V.size(); i--;) ret += itos(V.at(i),i+1==V.size()); return ret; } long long to_ll()const{ //long longへ. long long ret = vtoll(V); if(isMinus) ret = -ret; return ret; } __int128_t to_i128()const{ //int128へ. __int128_t ret = vtoll(V); if(isMinus) ret = -ret; return ret; } private: unsigned int size()const{return V.size();} //Vのsize(≠桁数)/ static bool isless(const vector &l,const vector &r){ //配列l &l,const vector &r){ //配列l<=r? if(l.size() != r.size()) return l.size() &v){return !v.size();} // v=0?; static bool isone(const vector &v){return v.size()==1&&v.at(0)==1;} //v=1? void shrink(){while(V.size() && V.back() == 0) V.pop_back();} //末尾0削除. static void shrink(vector &v){while(v.size() && v.back() == 0) v.pop_back();} //与えた配列の末尾0削除. static vector add(const vector &l,const vector &r){ //l+r vector ret(max(l.size(),r.size())+1); for(int i=0; i<(int)l.size(); i++) ret.at(i) += l.at(i); for(int i=0; i<(int)r.size(); i++) ret.at(i) += r.at(i); for(int i=0; i+1<(int)ret.size(); i++) if(ret.at(i) >= D) ret.at(i) -= D,ret.at(i+1)++; shrink(ret); return ret; } static vector sub(const vector &l,const vector &r){ //l-r(>=0) assert(isleEq(r,l)); //r<=l必須 vector ret(l); for(int i=0; i<(int)r.size(); i++){ ret.at(i) -= r.at(i); if(ret.at(i) < 0) ret.at(i) += D,ret.at(i+1)--; } for(int i=(int)r.size(); i<(int)l.size(); i++){ if(ret.at(i) < 0) ret.at(i) += D,ret.at(i+1)--; else break; } shrink(ret); return ret; } static vector mul(const vector &l,const vector &r){ //l*r if(l.size() == 0 || r.size() == 0) return {}; auto m = convolution_i128(l,r); vector ret; ret.reserve(m.size()+3); __int128_t bring = 0; for(int i=0; ; i++){ if(i >= m.size() && bring == 0) break; if(i < m.size()) bring += m.at(i); ret.push_back(bring%D),bring /= D; } shrink(ret); return ret; } static pair,vector> lldivll(const vector &l,const vector &r){ //{l/r,l%r} l,r<10^18 long long a = vtoll(l),b = vtoll(r); return {itov(a/b),itov(a%b)}; } static pair,vector> divint(const vector &l,const vector &r){ //{l/r,l%r} l,r<10^9 vector qu(l.size()); long long bring = 0,r1 = r.at(0); for(int i=(int)l.size(); i--;){ bring *= D,bring += l.at(i); qu.at(i) = bring/r1,bring %= r1; } shrink(qu); if(bring) return {qu,{(int)bring}}; return {qu,{}}; } static pair,vector> divnaive(const vector &l,const vector &r){ //{l/r,l%r} l,r<10^9 int n = l.size(),m = r.size(); if(max(n,m) <= 2) return lldivll(l,r); if(m == 1) return divint(l,r); if(isless(l,r)) return {{},l}; m = D/(r.back()+1); //r.back()を大きくして効率よくするらしいよ. vector l2 = mul(l,{m}),r2 = mul(r,{m}); n = l2.size(),m = r2.size(); vector qu(n-m+1),re(l2.end()-m,l2.end()); for(int i=n-m+1; i--;){ if(re.size() < r2.size()){} //持ち越しが足りない. else if(re.size() == r2.size()){ if(isleEq(r2,re)) qu.at(i) = 1,re = sub(re,r2); //re>r2の場合 r.back()>=D/2よりqu<=1. } else{ long long rv = (long long)re.back()*D+re.at(re.size()-2); int qv = rv/r2.back(); //これで精度めっちゃいいらしい. vector dec = mul(r2,{qv}); while(isless(re,dec)) qv--,dec = sub(dec,r2); //qvが大きすぎ. re = sub(re,dec); while(isleEq(r2,re)) qv++,re = sub(re,r2); //qvが小さすぎ. qu.at(i) = qv; } if(i) re.insert(re.begin(),l2.at(i-1)); } shrink(qu),shrink(re); return {qu,divint(re,{D/(r.back()+1)}).first}; //余りはr.back()を大きくした分割る. } static vector inv(const vector &v,int deg){ // 1/vのB^-degの絶対誤差. int k = deg,n = v.size(); while(k > 60) k = (k+1)/2; vector ret(n+k+1); ret.back() = 1; ret = divnaive(ret,v).first; //ここで10^-9*kの精度. //x<- 2*x - v*x^2で精度を高める. while(k < deg){ vector a = mul(ret,ret); //x^2. int d = min(n,2*k+1); //次の精度に合わせた桁数しか要らない. vector b(v.end()-d,v.end()),c = mul(a,b); //v*x^2. c.erase(c.begin(),c.begin()+d); //cはk+k+1要素以上ある?. vector x2(k); x2.reserve(k+k+1); for(auto v : add(ret,ret)) x2.push_back(v); ret = sub(x2,c),k *= 2; } ret.erase(ret.begin(),ret.begin()+k-deg); return ret; } static pair,vector> div(const vector &l,const vector &r){ //{l/r,l%r} assert(r.size()); int n = l.size(),m = r.size(); if(m <= 60 || n-m <= 60) return divnaive(l,r); //60以下で愚直. //ニュートン法. m = D/(r.back()+1); vector l2 = mul(l,{m}),r2 = mul(r,{m}); n = l2.size(),m = r2.size(); int deg = n-m+2; vector qu = mul(l2,inv(r2,deg)); qu.erase(qu.begin(),qu.begin()+n+2); vector dec = mul(r2,qu); while(isless(l2,dec)) qu = sub(qu,{1}),dec = sub(dec,r2); //quが大きい時. vector re = sub(l2,dec); while(isleEq(r2,re)) qu = add(qu,{1}),re = sub(re,r2); //quが小さい時. shrink(qu),shrink(re); return {qu,divint(re,{D/(r.back()+1)}).first}; //余りを割るのを忘れずに. } static string itos(int x,bool leading){ //整数xをstringへ 先頭要素なら0削除. assert(0 <= x && x < D); string ret = ""; for(int i=0; i static vector itov(T a){ //整数a(>=0)を配列vへ. assert(a >= 0); vector ret; while(a) ret.push_back(a%D),a /= D; return ret; } static long long vtoll(const vector &v){ //配列vをlong longへ. long long ret = 0; for(int i=v.size(); i--;) ret *= D,ret += v.at(i); return ret; } static __int128_t vtoi128(const vector &v){ //配列vをint128へ. __int128_t ret = 0; for(int i=v.size(); i--;) ret *= D,ret += v.at(i); return ret; } }; int main() { ios_base::sync_with_stdio(false); cin.tie(nullptr); using mint = modint<998>; const long long mod = 998; int T; cin >> T; while(T--){ int N,M; cin >> N >> M; if(N == 0){ while(M--){ string s; cin >> s; cout << "1\n"; } continue; } int loop = 1; mint sum = N; { mint v = N*N%mod; while(v != N) sum += v,v *= N,loop++; } while(M--){ string s; cin >> s; bigint v(s); auto [p,q] = getQR(v,loop); mint answer = 0; for(auto c : p.to_string()){ answer *= 10; answer += c-'0'; } answer *= sum; int left = q.to_ll(); mint x = N; while(left--){ answer += x; x *= N; } answer++; cout << answer << "\n"; } } }