結果
問題 | No.1857 Gacha Addiction |
ユーザー |
|
提出日時 | 2025-06-11 19:42:11 |
言語 | C++17 (gcc 13.3.0 + boost 1.87.0) |
結果 |
AC
|
実行時間 | 983 ms / 6,000 ms |
コード長 | 37,883 bytes |
コンパイル時間 | 5,245 ms |
コンパイル使用メモリ | 247,684 KB |
実行使用メモリ | 51,872 KB |
最終ジャッジ日時 | 2025-06-11 19:42:51 |
合計ジャッジ時間 | 34,635 ms |
ジャッジサーバーID (参考情報) |
judge2 / judge1 |
(要ログイン)
ファイルパターン | 結果 |
---|---|
sample | AC * 4 |
other | AC * 43 |
ソースコード
#include <bits/stdc++.h> using namespace std; //入力が必ず-mod<a<modの時. template<const int mod> struct modint{ //mod変更が不可能. public: long long v = 0; //void setmod(int m){} //飾り. static constexpr long long getmod(){return mod;} modint(){v = 0;} modint(int a){v = v<0?a+mod:a;} modint(long long a){v = v<0?a+mod:a;} modint(unsigned int a){v = a;} modint(unsigned long long a){v = a;} 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素数が必須. if(b == 0) assert(false); int left = mod-2; while(left){if(left&1) *this *= b; b *= b; left >>= 1;} return *this; } modint operator++(){*this += 1; return *this;} modint operator--(){*this -= 1; return *this;} modint operator++(int){*this += 1; return *this;} modint operator--(int){*this -= 1; return *this;} bool operator==(const modint b)const{return v == b.v;} bool operator!=(const modint b)const{return v != b.v;} bool operator>(const modint b)const{return v > b.v;} bool operator>=(const modint b)const{return v >= b.v;} bool operator<(const modint b)const{return v < b.v;} bool operator<=(const modint b)const{return v <= b.v;} 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 modint(1)/v;} //素数mod必須. }; template<int idx> //modが入力で与えられる場合. struct dynamic_modint{ //mod変更が可能 最初にsetmod必須 idxで複数個所持が可能. private: static int mod; public: long long v = 0; static constexpr long long getmod(){return mod;} static void setmod(int m,bool){ assert(m > 0); mod = m; } dynamic_modint(){v = 0;} dynamic_modint(int a){v = v<0?a+mod:a;} dynamic_modint(long long a){v = v<0?a+mod:a;} dynamic_modint(unsigned int a){v = a;} dynamic_modint(unsigned long long a){v = a;} long long val()const{return v;} dynamic_modint &operator=(const dynamic_modint &b) = default; dynamic_modint operator+()const{return (*this);} dynamic_modint operator-()const{return dynamic_modint(0)-(*this);} dynamic_modint operator+(const dynamic_modint b)const{return dynamic_modint(v)+=b;} dynamic_modint operator-(const dynamic_modint b)const{return dynamic_modint(v)-=b;} dynamic_modint operator*(const dynamic_modint b)const{return dynamic_modint(v)*=b;} dynamic_modint operator/(const dynamic_modint b)const{return dynamic_modint(v)/=b;} dynamic_modint operator+=(const dynamic_modint b){ v += b.v; if(v >= mod) v -= mod; return *this; } dynamic_modint operator-=(const dynamic_modint b){ v -= b.v; if(v < 0) v += mod; return *this; } dynamic_modint operator*=(const dynamic_modint b){v = v*b.v%mod; return *this;} dynamic_modint operator/=(dynamic_modint b){ //b!=0 mod素数が必須. if(b == 0) assert(false); int left = mod-2; while(left){if(left&1) *this *= b; b *= b; left >>= 1;} return *this; } dynamic_modint operator++(){*this += 1; return *this;} dynamic_modint operator--(){*this -= 1; return *this;} dynamic_modint operator++(int){*this += 1; return *this;} dynamic_modint operator--(int){*this -= 1; return *this;} bool operator==(const dynamic_modint b)const{return v == b.v;} bool operator!=(const dynamic_modint b)const{return v != b.v;} bool operator>(const dynamic_modint b)const{return v > b.v;} bool operator>=(const dynamic_modint b)const{return v >= b.v;} bool operator<(const dynamic_modint b)const{return v < b.v;} bool operator<=(const dynamic_modint b)const{return v <= b.v;} dynamic_modint pow(long long n)const{ dynamic_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; } dynamic_modint inv()const{return dynamic_modint(1)/v;} //素数mod必須. }; template<int idx> int dynamic_modint<idx>::mod=998244353; using mint = modint<998244353>; //using mint = modint<1000000007>; //using mint = dynamic_modint<0>; namespace to_fold{ __int128_t safemod(__int128_t a,long long m){a %= m; if(a < 0) a += m; return a;} pair<long long,long long> 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 mod> long long Garner(const vector<long long> &A,const vector<long long> &M){ __int128_t mulM = 1,x = A.at(0)%M.at(0); //Mの要素のペア互いに素必須. for(int i=1; i<A.size(); i++){ //assert(gcd(mulM,M.at(i-1)) == 1); mulM *= M.at(i-1); //2乗がオーバーフローする時__int128_t long long t = safemod((A.at(i)-x)*invgcd(mulM,M.at(i)).second,M.at(i)); x += t*mulM; } return x%mod; } int countzero(unsigned long long x){ if(x == 0) return 64; else return __popcount((x&-x)-1); } template<typename mint> 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<<i)); } }; template<typename mint> bool fftinfo<mint>::First=true; template<typename mint> mint fftinfo<mint>::g; template<typename mint> mint fftinfo<mint>::sum_e[30]; template<typename mint> mint fftinfo<mint>::sum_ie[30]; template<typename mint> mint fftinfo<mint>::divpow2[30]; template<typename mint> mint fftinfo<mint>::Zeta[30]; template<typename mint> void NTT(vector<mint> &A){ //ACLを超参考にしてる. int n = A.size(); assert((n&-n) == n); fftinfo<mint> 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<w; s++){ int offset = s<<(h-ph+1); for(int i=0; i<p; i++){ mint l = A.at(i+offset),r = A.at(i+offset+p)*rot; A.at(i+offset) = l+r; A.at(i+offset+p) = l-r; } rot *= info.sum_e[countzero(~(unsigned int)(s))]; } } } template<typename mint> void INTT(vector<mint> &A){ int n = A.size(); assert((n&-n) == n); fftinfo<mint> 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<w; s++){ int offset = s<<(h-ph+1); for(int i=0; i<p; i++){ mint l = A.at(i+offset),r = A.at(i+offset+p); A.at(i+offset) = l+r; A.at(i+offset+p) = ((unsigned long long)(mod+(unsigned int)l.v-(unsigned int)r.v)*irot.v)%mod; } irot *= info.sum_ie[countzero(~(unsigned int)(s))]; } } mint divn = info.divpow2[h]; for(auto &a : A) a *= divn; } template<typename mint> vector<mint> convolution(vector<mint> A,vector<mint> 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<mint> ret(sizc); if(siza >= sizb){for(int i=0; i<siza; i++) for(int k=0; k<sizb; k++) ret.at(i+k) += A.at(i)*B.at(k);} else{for(int i=0; i<sizb; i++) for(int k=0; k<siza; k++) ret.at(i+k) += B.at(i)*A.at(k);} return ret; } while(N < sizc) N <<= 1; A.resize(N),B.resize(N); NTT(A); NTT(B); for(int i=0; i<N; i++) A.at(i) *= B.at(i); INTT(A); A.resize(sizc); return A; } vector<long long> convolution_ll(const vector<long long> &A,const vector<long long> &B){ //long longに収まる範囲. int siza = A.size(),sizb = B.size(),sizc = siza+sizb-1; if(siza == 0 || sizb == 0) return {}; vector<long long> ret(sizc); if(min(siza,sizb) <= 200){ //naive 200はやばい?. vector<long long> ret(sizc); if(siza >= sizb){for(int i=0; i<siza; i++) for(int k=0; k<sizb; k++) ret.at(i+k) += A.at(i)*B.at(k);} else{for(int i=0; i<sizb; i++) for(int k=0; k<siza; k++) ret.at(i+k) += B.at(i)*A.at(k);} return ret; } const unsigned long long mod1 = 754974721,mod2 = 167772161,mod3 = 469762049; const unsigned long long m1m2 = mod1*mod2,m2m3 = mod2*mod3,m3m1 = mod3*mod1,m1m2m3 = mod1*mod2*mod3; const unsigned long long i1 = invgcd(m2m3,mod1).second,i2 = invgcd(m3m1,mod2).second,i3 = invgcd(m1m2,mod3).second; assert(sizc <= (1<<24)); using mint1 = modint<mod1>; using mint2 = modint<mod2>; using mint3 = modint<mod3>; vector<mint1> a1(siza),b1(sizb); vector<mint2> a2(siza),b2(sizb); vector<mint3> a3(siza),b3(sizb); for(int i=0; i<siza; i++) a1.at(i) = A.at(i)%mod1; for(int i=0; i<sizb; i++) b1.at(i) = B.at(i)%mod1; vector<mint1> C1 = convolution(a1,b1); for(int i=0; i<siza; i++) a2.at(i) = A.at(i)%mod2; for(int i=0; i<sizb; i++) b2.at(i) = B.at(i)%mod2; vector<mint2> C2 = convolution(a2,b2); for(int i=0; i<siza; i++) a3.at(i) = A.at(i)%mod3; for(int i=0; i<sizb; i++) b3.at(i) = B.at(i)%mod3; vector<mint3> C3 = convolution(a3,b3); vector<unsigned long long> offset = {0,0,m1m2m3,2*m1m2m3,3*m1m2m3}; for(int i=0; i<sizc; i++){ unsigned long long x = 0; x += (C1.at(i).v*i1)%mod1*m2m3; x += (C2.at(i).v*i2)%mod2*m3m1; x += (C3.at(i).v*i3)%mod3*m1m2; long long diff = C1.at(i).v-((long long)x+(long long)mod1)%mod1; if(diff < 0) diff += mod1; x -= offset.at(diff%5); ret.at(i) = x; } return ret; } template<typename mint> vector<mint> convolution_llmod(const vector<mint> &A,const vector<mint> &B){ int siza = A.size(),sizb = B.size(),sizc = siza+sizb-1; if(siza == 0 || sizb == 0) return {}; vector<mint> ret(sizc); if(min(siza,sizb) <= 200){ for(int i=0; i<siza; i++) for(int k=0; k<sizb; k++) ret.at(i+k) += A.at(i)*B.at(k); return ret; } const long long mod1 = 754974721,mod2 = 167772161,mod3 = 469762049; assert(sizc <= (1<<24)); using mint1 = modint<mod1>; using mint2 = modint<mod2>; using mint3 = modint<mod3>; vector<mint1> a1(siza),b1(sizb); vector<mint2> a2(siza),b2(sizb); vector<mint3> a3(siza),b3(sizb); for(int i=0; i<siza; i++) a1.at(i) = A.at(i).v%mod1; for(int i=0; i<sizb; i++) b1.at(i) = B.at(i).v%mod1; vector<mint1> C1 = convolution(a1,b1); for(int i=0; i<siza; i++) a2.at(i) = A.at(i).v%mod2; for(int i=0; i<sizb; i++) b2.at(i) = B.at(i).v%mod2; vector<mint2> C2 = convolution(a2,b2); for(int i=0; i<siza; i++) a3.at(i) = A.at(i).v%mod3; for(int i=0; i<sizb; i++) b3.at(i) = B.at(i).v%mod3; vector<mint3> C3 = convolution(a3,b3); for(int i=0; i<sizc; i++){ vector<long long> A = {C1.at(i).v,C2.at(i).v,C3.at(i).v}; vector<long long> M = {mod1,mod2,mod3}; ret.at(i) = Garner<mint::getmod()>(A,M); } return ret; } vector<int> convolution_int(const vector<int> &A,const vector<int> &B){ //intに収まる範囲. if(A.size() == 0 || B.size() == 0) return {}; vector<int> ret; if(min(A.size(),B.size()) <= 60){ ret.resize(A.size()+B.size()-1); for(int i=0; i<A.size(); i++) for(int k=0; k<B.size(); k++) ret.at(i+k) += A.at(i)*B.at(k); } else{ using mint1 = modint<998244353>; vector<mint1> X(A.size()),Y(B.size()),Z; for(int i=0; i<A.size(); i++) X.at(i) = A.at(i); for(int i=0; i<B.size(); i++) Y.at(i) = B.at(i); Z = convolution(X,Y); ret.resize(Z.size()); for(int i=0; i<Z.size(); i++) ret.at(i) = Z.at(i).v; } return ret; } template<typename mint> void NTTdoubling(vector<mint> &A){ //NTTの原理を忘れているため何やってるのか意味が分からない NTT-friendly専用. //INTT->resize(2倍)->NTTの代わりにcopy->INTT->謎の操作->NTT->push sizeが小さい時は効率悪いらしいよ. int n = A.size(); fftinfo<mint> info; vector<mint> 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; template<typename T> //実質mintだけ?. struct FormalPowerSeries:vector<T>{ //NTT-friendly素数だけ じゃなくてもいいけど全部書き直せ!. using vector<T>::vector; using fps = FormalPowerSeries; //重要なところは某のほぼパクリ. fps operator++(){*this += 1; return *this;} fps operator--(){*this -= 1; return *this;} fps operator++(int){*this += 1; return *this;} fps operator--(int){*this -= 1; return *this;} fps operator+(const fps &b) const {return fps(*this)+=b;} fps operator+(const T &b) const {return fps(*this)+=b;} fps operator-(const fps &b) const {return fps(*this)-=b;} fps operator-(const T &b) const {return fps(*this)-=b;} fps operator*(const fps &b){return fps(*this)*=b;} fps operator*(const T &b) const {return fps(*this)*=b;} fps operator/(const fps &b) const {return fps(*this)/=b;} fps operator%(const fps &b) const {return fps(*this)%=b;} fps operator>>(const unsigned int b) const {return fps(*this)>>=b;} fps operator<<(const unsigned int b) const {return fps(*this)<<=b;} fps operator-()const{ //-1倍; fps ret = (*this); for(auto &v : ret) v = -v; return ret; } bool operator==(const fps &b)const{ if((*this).size() != b.size()) return false; for(int i=0; i<(*this).size(); i++) if((*this).at(i) != b.at(i)) return false; return true; } bool operator!=(const fps &b)const{return !((*this)==b);} fps &operator+=(const fps &b){ //Cix^i = (Ai+Bi)x^i. O(n). if((*this).size() < b.size()) (*this).resize(b.size(),0); for(int i=0; i<b.size(); i++) (*this).at(i) += b.at(i); return *this; } fps &operator+=(const T &b){ //x^0の係数に+b O(1). if((*this).size() == 0) (*this).resize(1); (*this).at(0) += b; return *this; } fps &operator-=(const fps &b){ //Cix^i = (Ai-Bi)x^i. O(n). int n = b.size(); if((*this).size() < n) (*this).resize(n,0); for(int i=0; i<n; i++) (*this).at(i) -= b.at(i); return *this; } fps &operator-=(const T &b){ //x^0の係数に-b O(1). if((*this).size() == 0) (*this).resize(1); (*this).at(0) -= b; return *this; } fps &operator*=(const fps &b){ //C[i+k]x^(i+k) = Aix^i+Bkx^k. O(nlogn). vector<T> re; if(isNTTfriendly(mint::getmod())) re = convolution((*this),b); //NTT-friendlyならok 現在は4種以外認めない. else re = convolution_llmod((*this),b); (*this).resize(re.size()); for(int i=0; i<re.size(); i++) (*this).at(i) = re.at(i); return *this; } fps &operator*=(const T &b){ //x^iの係数に*b O(n). for(auto &v : (*this)) v *= b; return *this; } fps &operator/=(const T &b){ //x^iの係数に/b O(logmod+n). T mul = b.inv(); for(auto &v : (*this)) v *= mul; return *this; } fps &operator>>=(const unsigned int &b){//b<0は対象外. 先頭b項を削除. O(n) if((*this).size() <= b) (*this).clear(); else (*this).erase((*this).begin(),(*this).begin()+b); return *this; } fps &operator<<=(const unsigned int &b){//b<0は対象外. 先頭b項に0を挿入. O(n) (*this).insert((*this).begin(),b,0); return *this; } fps &operator%=(const fps &b){ //多項式の余り. O(nlogn) (*this) -= (*this)/b*b; del0(); return (*this); } fps &operator/=(const fps &b){ //多項式としての除算 O(nlogn). assert(b.size() > 0); //分母の末尾0は駄目. T check = b.back(); assert(check != 0); del0(); //分子の末尾0は消して許容. if((*this).size() < b.size()){ (*this).clear(); return *this; } int n = (*this).size()-b.size()+1; if(b.size() <= 64){ //愚直. fps G(b); assert(G.size() > 0); T div = G.back().inv(); for (auto &v : G) v *= div; int deg = (*this).size()-G.size()+1; fps Q(deg); for(int i=deg-1; i>=0; i--){ Q[i] = (*this).at(i+G.size()-1); for(int k=0; k<G.size(); k++) (*this).at(i+k) -= Q.at(i)*G.at(k); } (*this) = Q*div; (*this).resize(n,0); return *this; } //rev(f)/rev(g)≡rev(ret)(mod x^n)らしい わお. //998244353以外は定数倍悪い方に突っ込む. if(isNTTfriendly(mint::getmod())) return (*this) = ((*this).rev().prefix(n) * b.rev().inv(n)).prefix(n).rev(); return (*this) = ((*this).rev().prefix(n) * b.rev().inv2(n)).prefix(n).rev(); } fps multi_sparse(const fps &b)const{ //非0が少ない時の掛け算 愚直 O(n*|非0|+m). int n = (*this).size(),m = b.size(); vector<pair<int,T>> P; for(int i=0; i<m; i++) if(b.at(i) != 0) P.push_back({i,b.at(i)}); fps ret(n+m-1); for(int i=0; i<n; i++) for(auto [k,v] : P) ret.at(i+k) += (*this).at(i)*v; return ret; }; fps inv_sparse(int deg=-1)const{ //非0が少ない時の1/fを返す O(deg*|非0|+m+logmod). int n = (*this).size(); if(deg == -1) deg = n; assert((*this).at(0) != 0); T div = 1; vector<pair<int,T>> P; if((*this).at(0) != 1) div = T((*this).at(0)).inv(); for(int i=1; i<n; i++) if((*this).at(i) != 0) P.emplace_back(pair{i,(*this).at(i)*div}); fps ret(deg); ret.at(0) = 1; for(int i=1; i<deg; i++) for(auto [k,v] : P) if(i >= k) ret.at(i) -= v*ret.at(i-k); //-xが+ret[i-1]に対応. if(div != 1) for(auto &v : ret) v *= div; return ret; } fps inv_sparse(const fps &b,int deg = -1)const{ //f/gを返す 1/fでは分母だがこれは分子に注意. int n = (*this).size(),m = b.size(); if(deg == -1) deg = n; assert(b.at(0) != 0); T div = 1; vector<pair<int,T>> P; if(b.at(0) != 1) div = T(b.at(0)).inv(); for(int i=1; i<m; i++) if(b.at(i) != 0) P.emplace_back(pair{i,b.at(i)*div}); fps ret = (*this).prefix(deg); for(int i=1; i<deg; i++) for(auto [k,v] : P) if(i >= k) ret.at(i) -= v*ret.at(i-k); if(div != 1) for(auto &v : ret) v *= div; return ret; } fps log_sparse(int deg = -1){ //log(f)を返す O(N*非0). //logf = ∫(f'/f) inv,1/f*(f')がO(N*非0) 他はO(N). assert((*this).size()&&(*this).at(0)==1); if(deg == -1) deg = (*this).size(); fps ret = (*this).diff(); ret = ((*this).inv_sparse(deg)).multi_sparse(ret); return ret.inte().prefix(deg); } fps exp_sparse(int deg = -1)const{ //exp(f)を返す O(N*非0). //(expf)'=(f')*exp(f)より低次から決まる. //[x^0]expf=1より左辺のx^0の係数が求まる->expfのx^1の係数が求まる->... if(deg == -1) deg = (*this).size(); fps ret(deg); if((*this).size() == 0){ if(deg > 0) ret.at(0) = 1; return ret; } assert((*this).at(0) == 0); if(deg == 1) return fps{1}; ret.at(0) = 1; ret.at(1) = 1; const long long mod = mint::getmod(); for(int i=2; i<deg; i++) ret.at(i) = (-ret.at(mod%i)*(mod/i)); vector<pair<int,T>> P; for(int i=1; i<(*this).size(); i++) if((*this).at(i) != 0) P.emplace_back(pair{i-1,(*this).at(i)*i}); for(int i=0; i<deg-1; i++){ T now = 0; for(auto [k,v] : P){ if(i < k) break; now += ret.at(i-k)*v; } ret.at(i+1) *= now; } return ret; } fps pow_sparse(long long K,int deg = -1)const{ //f^Kを返す O(N*非0). //f^K = Fとする fF'= K*F*f'を使う. //[x^0]f=1に因数分解 (ax^i)^Kは別で処理. //n次はΣ[i=0~n]fi*F'[n-i]=K*Σ[i=1~n+1]F[n+1-i]*i*fi. //左辺のi=n以外移項 f0=1より F'n=K*Σ[i=1~n+1]F[n+1-i]*i*fi-Σ[i=1~n]fi*(n+1-i)*F[n+1-i]. //Fのn次まで分かっていればF'nが求まりFn+1も求まる F0=1からスタート. int n = (*this).size(); if(deg == -1) deg = (*this).size(); if(K == 0 || deg == 0){ fps ret(deg); if(deg > 0) ret.at(0) = 1; return ret; } for(int t=0; t<n; t++){ if((*this).at(t) != 0){ T div = T(1)/(*this).at(t); vector<pair<int,T>> P; for(int i=t+1; i<n; i++) if((*this).at(i) != 0) P.emplace_back(pair{i-t,(*this).at(i)*div}); fps ret(deg); ret.at(0) = 1; if(deg > 1) ret.at(1) = 1; const long long mod = mint::getmod(); for(int i=2; i<deg; i++) ret.at(i) = (-ret.at(mod%i)*(mod/i)); T mulK = K%mod; for(n=0; n<deg-1; n++){ //ここでnの役割が変わる注意. T now = 0; for(auto [i,v] : P){ if(i > n+1) break; if(i > 0 && i<=n+1) now += mulK*ret.at(n+1-i)*i*v; if(i > 0 && i <= n) now -= v*(n+1-i)*ret.at(n+1-i); } ret.at(n+1) *= now; } ret *= T((*this).at(t)).pow(K); return (ret<<(t*K)).prefix(deg); } if(K >= deg || (t+1)*K >= deg) break; } return fps(deg,0); } fps diff()const{ //微分 nx^(n-1) = (x^n)' O(n). int n = (*this).size(); if(n <= 1) return {}; fps ret(n-1); T multi = 1; for(int i=1; i<n; i++) ret.at(i-1) = (*this).at(i)*multi,multi++; return ret; } fps inte()const{ //積分. 1/(n+1)x^(n+1) = ∫x^n dx O(n). int n = (*this).size(); fps ret(n+1); ret.at(0) = 0; if(n == 0) return ret; ret.at(1) = 1; const long long mod = mint::getmod(); for(int i=2; i<=n; i++) ret.at(i) = (-ret.at(mod%i)*(mod/i)); //ここでret[i]=1/iになる. for(int i=0; i<n; i++) ret.at(i+1) *= (*this).at(i); return ret; } fps inv(int deg=-1)const{ //f*g=1 mod x^degとなるgを求める O(nlogn). if(isNTTfriendly(mint::getmod())){ //g.pre(2m)<- (2g.pre(m)-f*g.pre(m)*g.pre(m))mod x^2mとする. //fgm≡1 mod x^mからfg2m≡1 mod x^2mが求まる ダブリング. //g2m<- gm-((fgm-1)*gm)を求める. if((*this).size() <= 100) return inv_sparse(deg); assert((*this).at(0) != 0); if(deg == -1) deg = (*this).size(); fps ret(deg); ret.at(0) = T(1)/(*this).at(0); for(int i=1; i<deg; i<<=1){ //NTT-friendlyじゃないと駄目らしい. fps f(2*i),g(2*i); int n = min(2*i,(int)(*this).size()); for(int k=0; k<n; k++) f.at(k) = (*this).at(k); for(int k=0; k<i; k++) g.at(k) = ret.at(k); NTT(f),NTT(g); for(int k=0; k<2*i; k++) f.at(k) *= g.at(k); //(fgm-1)を計算. INTT(f); for(int k=0; k<i; k++) f.at(k) = 0; //[m,2m)の項だけにする([0,m)は一致してない). NTT(f); for(int k=0; k<2*i; k++) f.at(k) *= g.at(k); //(fgm-1)*gmを計算. INTT(f); n = min(2*i,deg); for(int k=i; k<n; k++) ret.at(k) = -f.at(k); //-(fgm^2-gm)が入る. } return ret; //ね、簡単でしょ?. } else{ //g.pre(2m)<- (2g.pre(m)-f*g.pre(m)*g.pre(m))mod x^2mとする. //fgm≡1 mod x^mからfg2m≡1 mod x^2mが求まる ダブリング. //こっちは愚直にやる NTT-friendlyじゃないならllmodのconvolutionに変更しないと使えない. if((*this).size() <= 200) return inv_sparse(deg); assert((*this).at(0) != 0); if(deg == -1) deg = (*this).size(); fps ret(1); ret.at(0) = T(1)/(*this).at(0); for(int i=1; i<deg; i<<=1) ret = (ret+ret-ret*ret*((*this).prefix(i<<1))).prefix(i<<1); return ret.prefix(deg); } } fps log(int deg=-1)const{ //logfを求める O(nlogn). //log(1-f) = -Σ[n=1~∞](f^n/n)と定義する [x^0]f=1&n<modが条件. //(logf)'=f'/f,log(fg)=logf+loggが成り立つ. assert((*this).size()&&(*this).at(0)==1); if(deg == -1) deg = (*this).size(); return ((*this).diff()*(*this).inv(deg)).prefix(deg-1).inte(); //∫f'/fから求める. } fps exp(int deg=-1)const{ //expfを求める O(nlogn). if(isNTTfriendly(mint::getmod())){ //expf=Σ[n=0~∞]f^n/n!と定める [x^0]f=0,n<modが条件. //(expf)'= f'expf,exp(f+g)=expf*expg,log(expf)=fを満たす (各々条件は忘れずに). //ニュートン法で求める g2m<-gm*(1-log(gm)+f) mod x^2nに更新 納得してない. assert((*this).size() == 0 || (*this)[0] == mint(0)); if (deg == -1) deg = (*this).size(); vector<T> divi; //invと衝突回避用 mintでiの逆元. const long long mod = mint::getmod(); divi.resize(deg*2); divi.at(1) = 1; for(int i=2; i<deg*2; i++) divi.at(i) = ((-divi.at(mod%i)))*(mod/i); auto integral = [&](fps &f) -> void { //inplaceで積分. int n = f.size(); f.insert(f.begin(),0); for(int i=1; i<=n; i++) f.at(i) *= divi.at(i); }; auto differential = [&](fps &f) -> void { //inplaceで微分. if(f.size() == 0) return; f.erase(f.begin()); T multi = 0; for(int i=0; i<f.size(); i++) f.at(i) *= ++multi; }; fps f({1}),g({1}),z1,z2({1,1}); //何やってるか本当に意味わからずコメント書けない 後で確認. if((*this).size() > 1) f.push_back((*this).at(1)); else f.push_back(0); for(int m=2; m<deg; m<<=1){ auto ff = f; ff.resize(2*m); NTT(ff); z1 = z2; fps z(m); for(int i=0; i<m; i++) z.at(i) = ff.at(i)*z1.at(i); INTT(z); for(int i=0; i<m/2; i++) z.at(i) = 0; NTT(z); for(int i=0; i<m; i++) z.at(i) *= -z1.at(i); INTT(z); for(int i=m/2; i<m; i++) g.emplace_back(z.at(i)); z2 = g; z2.resize(2*m); NTT(z2); fps h = (*this).prefix(m); differential(h); h.emplace_back(T(0)); NTT(h); for(int i=0; i<m; i++) h.at(i) *= ff.at(i); INTT(h); h -= f.diff(); h.resize(2*m); for(int i=0; i<m-1; i++) h.at(m+i) = h.at(i),h.at(i) = 0; NTT(h); for(int i=0; i<2*m; i++) h.at(i) *= z2.at(i); INTT(h); h.pop_back(); integral(h); int n = min((int)(*this).size(),2*m); for(int i=m; i<n; i++) h.at(i) += (*this).at(i); for(int i=0; i<m; i++) h.at(i) = 0; NTT(h); for(int i=0; i<2*m; i++) h.at(i) *= ff.at(i); INTT(h); for(int i=m; i<2*m; i++) f.emplace_back(h.at(i)); } return f.prefix(deg); } else{ //NTT-friendlyじゃないとK=10^5で1sec以上かかる 畳み込みが遅い. assert((*this).size() == 0 || (*this).at(0) == 0); if(deg == -1) deg = (*this).size(); fps ret(1); ret.at(0) = 1; for(int i=1; i<deg; i<<=1){ //ニュートン法 g2m<-gm*(1-log(gm)+f) mod x^2n 再掲. ret = (ret*((*this).prefix(2*i)+T(1)-ret.log(2*i))).prefix(2*i); //logの引数普段と違う. } return ret.prefix(deg); } } fps pow(long long K,int deg=-1)const{ //K乗を返すO(nlogn) mod10^9+7で3番目引数false. //f^k = exp(klog(f))を使う. //logfが計算できるように調整する. int n = (*this).size(); if(deg == -1) deg = n; if(K == 0){ //0乗はx^0だけ係数1. fps ret(deg); if(deg > 0) ret.at(0) = 1; return ret; } for(int i=0; i<n; i++){ if((*this).at(i) != 0){ //0じゃなかったらOK. const long long mod = mint::getmod(); T div = T(1)/(*this).at(i); //*([x^i]f)^Kと *x^(i*k)の分は後回し. fps ret = ((((*this)*div)>>i).log(deg)*(K%mod)).exp(deg); ret *= T((*this).at(i)).pow(K); //[x^i]f^Kの分. ret = (ret<<(i*K)).prefix(deg); //*x^(i*k)の分. return ret; } if(K >= deg || (i+1)*K >= deg) break; //((i+1)*K)乗未満は0確定 int128回避用にK>=deg(degがllはやばい). } return fps(deg,0); //fの係数全て0なら係数全て0. } fps prefix(int siz)const{ //先頭siz項を返す なかったら0埋め. O(siz). fps ret((*this).begin(),(*this).begin()+min((int)(*this).size(),siz)); if(ret.size() < siz) ret.resize(siz,0); return ret; } void del0(){ //末尾の0を消す O(n). while((*this).size() && (*this).back() == 0) (*this).pop_back(); } fps rev()const{ //ひっくり返す O(n). fps ret(*this); reverse(ret.begin(),ret.end()); return ret; } pair<fps,fps> getQR(const fps &b)const{ //多項式の商と余りを同時に得る O(nlogn). fps Q = (*this)/b,R = (*this)-Q*b; R.del0(); return {Q,R}; } fps cumulativeNtimes(int N,T b,int deg=-1){ //1/(1-bx)^Nをdeg次まで返す 指定なしはN次まで. //負の二項定理を使う. 1/(1-x)^N=Σ[i=0~∞]((n+i-1) choose i)(bx^i); //fps{}.cumulativeNtime()で無理やり関数を呼び出す. assert(N <= 0); //N=0も駄目? {1}を返すべき所{0}になる. if(deg == -1) deg = N+1; int Limit = N+deg; //Limit 必要なサイズ fac->x! facinv->1/x! inv->1/x. long long mod = mint::getmod(),invstart = min((int)mod-1,Limit); vector<T> FAC(Limit+1,1); for(int i=1; i<=Limit; i++) FAC.at(i) = FAC.at(i-1)*i; vector<T> FACinv(Limit+1); FACinv.at(invstart) = FAC.at(invstart).inv(); for(int i=invstart-1; i>=0; i--) FACinv.at(i) = FACinv.at(i+1)*(i+1); auto nCr = [&](int n, int r) -> T { if(n < r || r < 0 || n < 0) return 0; return FAC.at(n)*FACinv.at(r)*FACinv.at(n-r); }; fps ret(deg); T value = 1; for(int i=0; i<deg; i++) ret.at(i) = nCr(N+i-1,i)*value,value *= b; return ret; } fps Taylorshift(long long C)const{ //Σ[i=0~n]Ai*(x+c)^iを返す O(nlogn). //Σ[i=0~n]x^i/i! Σ[k=0~n-i](A[i+k]*(i+k)!)*(c^k/k!)になる. //kの部分はXi=Ai*i!,Yi=c^i/i!として.Zi=ΣX[i+k]Yk. //これは[x^(i+n)]X*rev(Y)で求められる. const long long mod = mint::getmod(); mint c = (C%mod+mod)%mod,pc = 1; if(c == 0) return (*this); int deg = (*this).size(); //deg 必要なサイズ fac->x! facinv->1/x! inv->1/x. long long invstart = min((int)mod-1,deg); vector<mint> fac(deg+1,1); for(int i=1; i<=deg; i++) fac.at(i) = fac.at(i-1)*i; vector<mint> facinv(deg+1); facinv.at(invstart) = fac.at(invstart).inv(); for(int i=invstart-1; i>=0; i--) facinv.at(i) = facinv.at(i+1)*(i+1); fps X(deg),Y(deg); for(int i=0; i<deg; i++) X.at(i) = (*this).at(i)*fac.at(i),Y.at(deg-1-i) = pc*facinv.at(i),pc *= c; X *= Y; fps ret(deg); for(int i=0; i<deg; i++) ret.at(i) = X.at(i+deg-1)*facinv.at(i); return ret; } }; using fps = FormalPowerSeries<mint>; //mod998244353以外のNTT-friendlyの時は色々気を付ける 後で書き直すかも?. template<typename mint> pair<vector<mint>,vector<mint>> SumFraction(vector<vector<mint>> A,vector<vector<mint>> B){ //次数の和=n O(nlog^2n). //ΣAi/Biを求める 分割統治でO(nlog^2n). //a/b+c/d = (ad+bc)/bd. assert(A.size() == B.size() && A.size()); int n = A.size(); auto f = [&](auto f,int l,int r) -> pair<vector<mint>,vector<mint>> { if(l+1 == r) return {A.at(l),B.at(l)}; auto [al,bl] = f(f,l,(l+r)/2); auto [ar,br] = f(f,(l+r)/2,r); vector<mint> retA = convolution(al,br); vector<mint> retA2 = convolution(bl,ar); retA.resize(max(retA.size(),retA2.size())); for(int i=0; i<retA2.size(); i++) retA.at(i) += retA2.at(i); return {retA,convolution(bl,br)}; }; auto f2 = [&](auto f2,int l,int r) -> pair<vector<mint>,vector<mint>> { if(l+1 == r) return {A.at(l),B.at(l)}; auto [al,bl] = f2(f2,l,(l+r)/2); auto [ar,br] = f2(f2,(l+r)/2,r); vector<mint> retA = convolution_llmod(al,br); vector<mint> retA2 = convolution_llmod(bl,ar); retA.resize(max(retA.size(),retA2.size())); for(int i=0; i<retA2.size(); i++) retA.at(i) += retA2.at(i); return {retA,convolution_llmod(bl,br)}; }; if(isNTTfriendly(mint::getmod())) return f(f,0,n); else return f2(f2,0,n); } pair<fps,fps> SumFraction(vector<fps> A,vector<fps> B){ //次数の和=n O(nlog^2n). //ΣAi/Biを求める 分割統治でO(nlog^2n). //a/b+c/d = (ad+bc)/bd. assert(A.size() == B.size() && A.size()); int n = A.size(); auto f = [&](auto f,int l,int r) -> pair<fps,fps> { if(l+1 == r) return {A.at(l),B.at(l)}; auto [al,bl] = f(f,l,(l+r)/2); auto [ar,br] = f(f,(l+r)/2,r); return {al*br+bl*ar,bl*br}; }; return f(f,0,n); } istream &operator>>(istream &is,mint &a){ long long aa; cin >> aa; a = aa; return is; } template<typename T> istream &operator>>(istream &is,vector<T> &a){ for(auto &v : a) cin >> v; return is; } ostream &operator<<(ostream &os,const mint &a){cout << a.val(); return os;} template<typename T> ostream &operator<<(ostream &os,const vector<T> &a){ if(a.size() == 0) return os; cout << a.at(0); for(int i=1; i<a.size(); i++) cout << " " << a.at(i); return os; } int main(){ ios_base::sync_with_stdio(false); cin.tie(nullptr); int N,S; cin >> N; S = N; cin >> S; vector<vector<mint>> A(N),B(N); mint divS = mint(1)/S; for(int i=0; i<N; i++){ int p = 1; cin >> p; mint one = divS*p,two = one*one; A.at(i) = {two}; B.at(i) = {1,one}; } auto [n,d] = SumFraction(A,B); mint answer = 0,fac = 1; for(int i=0; i<n.size(); i++) answer += n.at(i)*fac*(i+2),fac *= i+2; cout << answer.v << endl; }