有了 NTT,就有了多项式全家桶……
首先要感谢 @command_block 的文章《NTT 与多项式全家桶》以及 @Epsilon_Cube、@MoYuFang 和 @Diu 给予我的许多帮助。
因为板子在不断修 bug,所以代码最后统一放。
前置芝士
- 多项式的各种运算是怎么定义的
由于我们只知道多项式加法和多项式乘法,但是这已经够了。所以所有的多项式运算都是用多项式加法和乘法定义的。
- 次数界
很多时候我们只对多项式 的前 项感兴趣(这时往往 会有无限项),所以需要在 的意义下运算。
由于多项式加法和多项式乘法的结果只会从低次项向高次项贡献,所以有:
即我们可以在有次数界的情况下定义所有多项式运算。
一些记号
- :多项式 的 次项的系数,即 的系数;
- : 次多项式的翻转 ,显然 的系数是 的系数的翻转;
- :多项式 的 阶导数,即对 求导 次的结果;
多项式求导和积分
定义多项式的求导:
定义多项式的积分(不定积分):
同样的,多项式求导和积分也是互为逆操作。
多项式牛顿迭代
这是一个比较重要的知识,有了它,就可以无脑推多项式各种操作的递推式了。
形式:已知函数 满足 ,求 。
实践中 一般较为手动构造的简单函数。
结论:,其中 ,注意 的解要单独求出。
和一般的牛迭十分相似,但是次数每次翻倍。证明如下:
假设目前已经求出了 ,考虑 在 处的泰勒展开:
注意到 的最低系数非 项至少是 ,那么对于所有 的 都有 ,所以:
证毕。
多项式乘法逆
假设已经求出了 ,现在要求 ,那么有:
则可以直接套牛顿迭代:
那么就可以做了, 需要求一次乘法逆元,时间复杂度为 。
不过还有个优化,注意到 NTT 的过程代入的是单位根,所以求的实际上是循环卷积:
观察倍增式子:
需要用到乘法的只有 。
先计算 ,它们的次数分别是 和 。
由于结果的第一项为 ,这个 后面的 项都为 ,所以长度为 的循环卷积只会破坏前面的 和 。
最后乘上一个 即可,此时循环卷积只会破坏前 项。
多项式开根
假设已经求出了 ,现在要求 ,那么有:
直接套牛迭:
最后 需要求一次二次剩余,可以用 BSGS/exBSGS 求单位根的高次同余方程来求解,再加上一个求逆,一个乘法,一个加法就做完了。
时间复杂度为 。
多项式
两边同时求导,得:
注意到 ,所以:
再积分回来:
所以一个求导,一个逆元,一个乘法,一个积分即可。
注意由于 ,所以有 。并且若 则无法求 因为求不出模意义下的 。
时间复杂度 。
多项式
我们设 ,那么显然 ,可以使用牛顿迭代了。
回忆牛迭式子:
显然,这里的 ,那么假设我们已经求出了 ,有:
所以倍增求即可。
注意由于 ,所以有 。并且若 则无法求 因为求不出模意义下的 。
时间复杂度为 。
多项式快速幂
观察到 ,所以一个 ,一个逐项乘法,一个 就做完了。
这题和上一题的区别在于有 的情况,这时我们就没办法求 和 了。
但是 没关系,我们可以让所有项都乘上 ,最后再都乘上 即可。
遇到 的情况也没关系,把系数往前移,求出答案后再移回去即可。不过要注意原来前面 个 在做幂运算后会变成 个 。
多项式带余除法
发现余数很烦,所以我们想办法去掉它。
舍弃多项式的项的方法是一般是加上次数界,但注意到次数界只能舍弃高次,所以考虑把多项式的系数反过来搞。
那么回到题目的式子:
其中 是 次多项式(已知), 是 次多项式(已知), 是 次多项式(未知), 是 次多项式(未知)。
换元,有:
同乘 ,有:
发现 ,,,所以有:
那么我们机智地加上次数界, 上 ,就有:
那么就可以求出 了,系数反过来就是 ,然后即可用乘法和减法求出 ,时间复杂度 。
完整模板
包括多项式多点求值、多项式快速插值。
展开
const int p=998244353,ginv=332748118;
inline void add(int &x,int y)
{
x+=y;
if(x>=p) x-=p;
}
inline int gcd(int a,int b)
{
int t=a%b;
while(t!=0) a=b,b=t,t=a%b;
return b;
}
inline int qpow(int x,int y)
{
int res=1;
for(;y>0;y>>=1) res=((y&1)?1ll*res*x%p:res),x=1ll*x*x%p;
return res;
}
inline int exBSGS(int a,int b,int p)
{
a%=p,b%=p;
if(b==1||p==1) return 0;
int cnt=0,val=1;
while(1)
{
int d=gcd(a,p);
if(d==1) break;
if(b%d!=0) return -1;
p/=d;
b/=d;
val=1ll*val*(a/d)%p;
cnt++;
if(val==b) return cnt;
}
map<int,int> mp;
int val2=1,t=sqrt(p)+1;
for(int B=1;B<=t;B++)
{
val2=1ll*val2*a%p;
mp[1ll*b*val2%p]=B;
}
int cur=val;
for(int A=1;A<=t;A++)
{
cur=1ll*cur*val2%p;
if(mp.find(cur)!=mp.end()) return A*t-mp[cur]+cnt;
}
return -1;
}
inline int mosqrt(int x)
{
int bse=exBSGS(3,x,p);
if(bse==-1||(bse&1)) return -1;
return qpow(3,bse/2);
}
namespace PLOY
{
const int MS=5000005;
typedef vector<int> ploy;
inline ploy operator+(ploy a,ploy b);
inline ploy operator+(ploy a,int b);
inline ploy operator+(int a,ploy b);
inline ploy operator-(ploy a,ploy b);
inline ploy operator-(ploy a,int b);
inline ploy operator-(int a,ploy b);
inline ploy operator*(int b,ploy a);
inline ploy operator*(ploy a,int b);
inline ploy operator*(int b,ploy a);
inline ploy inv(ploy a);
inline ploy dao(ploy a);
inline ploy jif(ploy a);
inline ploy ln(ploy a);
inline ploy exp(ploy a);
inline ploy pow(ploy a,int b);
inline ploy pow2(ploy a,int b,int b2); // b%p b2%(p-1)
inline void divi(ploy a,ploy b,ploy &res,ploy &r);
inline ploy operator%(ploy a,ploy b);
inline vector<int> getval(ploy a,vector<int> x);
inline ploy getploy(vector<int> x,vector<int> y);
int p_rev[MS],p_rev_lstn;
int p_tmpinv[MS];
inline int getlen(int n)
{
int res=1;
while(res<n) res<<=1;
return res;
}
inline void NTT(ploy &a,int tpe)
{
int n=a.size();
if(p_rev_lstn!=n)
{
p_rev_lstn=n;
for(int i=0;i<n;i++) p_rev[i]=(p_rev[i>>1]>>1)|((i&1)?n>>1:0);
}
for(int i=0;i<n;i++) if(p_rev[i]<i) swap(a[p_rev[i]],a[i]);
int g=tpe==1?3:ginv;
for(int mid=1;mid<n;mid<<=1)
{
int len=mid<<1,Wn=qpow(g,(p-1)/len);
for(int l=0;l<n-len+1;l+=len)
{
for(int k=0,Wk=1;k<mid;k++,Wk=1ll*Wk*Wn%p)
{
int x=a[l+k],y=1ll*Wk*a[l+mid+k]%p;
a[l+k]=(x+y)%p,a[l+mid+k]=(x-y+p)%p;
}
}
}
}
inline void DFT(ploy &a){NTT(a,1);}
inline void IDFT(ploy &a)
{
int n=a.size();
NTT(a,-1);
int inv=qpow(n,p-2);
for(int i=0;i<n;i++) a[i]=1ll*a[i]*inv%p;
}
inline ploy operator+(ploy a,ploy b)
{
if(a.size()<b.size()) swap(a,b);
for(int i=0;i<b.size();i++) add(a[i],b[i]);
return a;
}
inline ploy operator+(ploy a,int b){return add(a[0],b),a;}
inline ploy operator+(int a,ploy b){return add(b[0],a),b;}
inline ploy operator-(ploy a,ploy b)
{
if(a.size()<b.size()) a.resize(b.size(),0);
for(int i=0;i<b.size();i++) add(a[i],p-b[i]);
return a;
}
inline ploy operator-(ploy a,int b){return add(a[0],p-b),a;}
inline ploy operator-(int a,ploy b)
{
add(b[0],p-a);
for(int i=0;i<b.size();i++) b[i]=p-b[i];
return b;
}
inline ploy operator*(ploy a,int b)
{
for(int i=0;i<a.size();i++) a[i]=1ll*a[i]*b%p;
return a;
}
inline ploy operator*(int b,ploy a)
{
for(int i=0;i<a.size();i++) a[i]=1ll*a[i]*b%p;
return a;
}
inline ploy operator*(ploy a,ploy b)
{
int n=a.size()+b.size()-1,m=getlen(n);
a.resize(m,0),b.resize(m,0);
DFT(a),DFT(b);
for(int i=0;i<m;i++) a[i]=1ll*a[i]*b[i]%p;
IDFT(a);
a.resize(n,0);
return a;
}
inline ploy inv(ploy a)
{
int n=a.size(),m=getlen(n);
ploy res={qpow(a[0],p-2)};
for(int len=2;len<=m;len<<=1)
{
ploy tmp=a;
tmp.resize(len,0);
res=res*2-res*res*tmp;
res.resize(len,0);
}
res.resize(n,0);
return res;
}
inline ploy sqrt(ploy a)
{
ploy res={mosqrt(a[0])};
if(res[0]==-1) return ploy();
int n=a.size(),m=getlen(n)*2; // 不知道为什么要乘二
for(int len=2;len<=m;len<<=1)
{
ploy tmp=a;
tmp.resize(len,0);
res=(res*res+tmp)*inv(res*2);
res.resize(len,0);
}
res.resize(n,0);
return res;
}
inline ploy dao(ploy a)
{
int n=a.size();ploy res=a;
res[n-1]=0;for(int i=1;i<n;i++) res[i-1]=1ll*a[i]*i%p;
return res;
}
inline ploy jif(ploy a)
{
int n=a.size();ploy res=a;
for(int i=1;i<n;i++) if(p_tmpinv[i]==0) p_tmpinv[i]=(i==1?1:1ll*p_tmpinv[p%i]*(p-p/i)%p);
res[0]=0;for(int i=1;i<n;i++) res[i]=1ll*a[i-1]*p_tmpinv[i]%p;
return res;
}
inline ploy ln(ploy a)
{
int n=a.size();
ploy res=jif(dao(a)*inv(a));
res.resize(n,0);
return res;
}
inline ploy exp(ploy a)
{
int n=a.size(),m=getlen(n)*2;
ploy res={1};
for(int len=2;len<=m;len<<=1)
{
ploy tmp=a;
tmp.resize(len,0);
res=(1-ln(res)+tmp)*res;
res.resize(len,0);
}
res.resize(n,0);
return res;
}
inline ploy pow(ploy a,int b)
{
ploy tmp=ln(a);
for(int i=0;i<a.size();i++) tmp[i]=1ll*tmp[i]*b%p;
return exp(tmp);
}
inline ploy pow2(ploy a,int b,int b2)
{
int n=a.size(),cnt=0;
for(int i=0;i<n&&a[i]==0;i++) cnt++;
if(1ll*cnt*b2>=n) return ploy(n,0);
int pos=cnt*b2,m=n-pos;
ploy tmp;
for(int i=cnt;i<cnt+m;i++) tmp.push_back(a[i]);
int inv=qpow(tmp[0],p-2),ml=qpow(tmp[0],b2);
for(int i=0;i<m;i++) tmp[i]=1ll*tmp[i]*inv%p;
tmp=pow(tmp,b);
for(int i=0;i<m;i++) tmp[i]=1ll*tmp[i]*ml%p;
ploy res(pos,0);
for(int i=0;i<m;i++) res.push_back(tmp[i]);
return res;
}
inline void divi(ploy a,ploy b,ploy &res,ploy &r)
{
int n=a.size(),m=b.size();
if(n<m) return res={0},r=a,void();
int rl=n-m+1;
ploy ta=a,tb=b;
reverse(ta.begin(),ta.end()),reverse(tb.begin(),tb.end());
ta.resize(rl,0),tb.resize(rl,0);
res=ta*inv(tb);
res.resize(rl,0);
reverse(res.begin(),res.end());
r=a-b*res;
r.resize(m-1,0);
}
inline ploy operator%(ploy a,ploy b)
{
ploy res,r;
divi(a,b,res,r);
return r;
}
inline vector<int> getval(ploy a,vector<int> x)
{
int n=x.size();
vector<ploy> ml(n<<2|1),res(n<<2|1);
vector<pair<pair<int,int>,pair<int,int> > > sta;
vector<int> ans(n);
sta.emplace_back(make_pair(1,0),make_pair(0,n-1));
while(!sta.empty())
{
auto t=*sta.rbegin();
sta.pop_back();
int u=t.first.first,stp=t.first.second,l=t.second.first,r=t.second.second;
if(l==r)
{
ml[u]={p-x[l],1};
continue;
}
int mid=l+r>>1;
if(stp==0)
{
t.first.second++;
sta.push_back(t);
sta.emplace_back(make_pair(u<<1,0),make_pair(l,mid));
}
else if(stp==1)
{
t.first.second++;
sta.push_back(t);
sta.emplace_back(make_pair(u<<1|1,0),make_pair(mid+1,r));
}
else ml[u]=ml[u<<1]*ml[u<<1|1];
}
res[1]=a%ml[1];
sta.emplace_back(make_pair(1,0),make_pair(0,n-1));
while(!sta.empty())
{
auto t=*sta.rbegin();
sta.pop_back();
int u=t.first.first,l=t.second.first,r=t.second.second;
if(l==r)
{
ans[l]=res[u][0];
continue;
}
int mid=l+r>>1;
res[u<<1]=res[u]%ml[u<<1];
res[u<<1|1]=res[u]%ml[u<<1|1];
sta.emplace_back(make_pair(u<<1,0),make_pair(l,mid));
sta.emplace_back(make_pair(u<<1|1,0),make_pair(mid+1,r));
}
return ans;
}
inline ploy getploy(vector<int> x,vector<int> y)
{
int n=x.size();
vector<ploy> ml(n<<2|1),res(n<<2|1);
vector<pair<pair<int,int>,pair<int,int> > > sta;
vector<int> ans(n);
sta.emplace_back(make_pair(1,0),make_pair(0,n-1));
while(!sta.empty())
{
auto t=*sta.rbegin();
sta.pop_back();
int u=t.first.first,stp=t.first.second,l=t.second.first,r=t.second.second;
if(l==r)
{
ml[u]={p-x[l],1};
continue;
}
int mid=l+r>>1;
if(stp==0)
{
t.first.second++;
sta.push_back(t);
sta.emplace_back(make_pair(u<<1,0),make_pair(l,mid));
}
else if(stp==1)
{
t.first.second++;
sta.push_back(t);
sta.emplace_back(make_pair(u<<1|1,0),make_pair(mid+1,r));
}
else ml[u]=ml[u<<1]*ml[u<<1|1];
}
ploy M=dao(ml[1]);
vector<int> val=getval(M,x);
for(int i=0;i<n;i++) val[i]=1ll*qpow(val[i],p-2)*y[i]%p;
sta.emplace_back(make_pair(1,0),make_pair(0,n-1));
while(!sta.empty())
{
auto t=*sta.rbegin();
sta.pop_back();
int u=t.first.first,stp=t.first.second,l=t.second.first,r=t.second.second;
if(l==r)
{
res[u]={val[l]};
continue;
}
int mid=l+r>>1;
if(stp==0)
{
t.first.second++;
sta.push_back(t);
sta.emplace_back(make_pair(u<<1,0),make_pair(l,mid));
}
else if(stp==1)
{
t.first.second++;
sta.push_back(t);
sta.emplace_back(make_pair(u<<1|1,0),make_pair(mid+1,r));
}
else res[u]=res[u<<1]*ml[u<<1|1]+ml[u<<1]*res[u<<1|1];
}
return res[1];
}
}
更快的板子
展开
#include <bits/stdc++.h>
using ull = unsigned long long;
const int N = 280000;
const int Mod = 998244353;
typedef std::vector<int> Poly;
namespace Pol {
int pow(int a, int b, int ans = 1);
int add(int a, int b) {
return (a += b) >= Mod ? a -= Mod : a;
}
int sub(int a, int b) {
return (a -= b) < 0 ? a += Mod : a;
}
void inc(int &a, int b) {
(a += b) >= Mod ? a -= Mod : a;
}
void dec(int &a, int b) {
(a -= b) < 0 ? a += Mod : a;
}
void init_Poly(int n = N);
void DIT(int *A, int lim);
void DIF(int *A, int lim);
Poly inv(Poly A, int n);
Poly mult(const Poly &A, int n, const Poly &B, int m);
Poly operator*(const Poly &A, const Poly &B) {
return mult(A, A.size(), B, B.size());
}
Poly Tmul(const Poly &A, int n, const Poly &B, int m);
Poly getv(Poly A, int n, const std::vector<int> &f, int m);
Poly drv(const Poly &A, int n);
Poly itg(const Poly &A, int n);
Poly ln(const Poly &A, int n);
Poly exp(Poly A, int n);
int fac[N], ifac[N], iv[N];
Poly G[N << 1];
ull tmp[N];
int gw[N];
} // namespace Pol
int main() {
Pol::init_Poly();
int n, m;
scanf("%d %d", &n, &m);
Poly F(n);
for (int i = 0; i < n; ++i) scanf("%d", &F[i]);
Poly G = Pol::ln(Pol::inv(Pol::exp(F, n), n), n);
for (int i = 0; i < n; ++i) printf("%d%c", G[i], " \n"[i == n - 1]);
std::vector<int> f(m);
for (int i = 0; i < m; ++i) scanf("%d", &f[i]);
G = Pol::getv(F, n, f, m);
for (int i = 0; i < m; ++i) printf("%d%c", G[i], " \n"[i == m - 1]);
return 0;
}
namespace Pol {
void DIT(int *A, int lim) {
for (int i = 0; i < lim; ++i) tmp[i] = A[i];
for (int l = 1; l < lim; l <<= 1) {
ull *k = tmp;
for (int *g = gw; k < tmp + lim; k += (l << 1), ++g) {
for (ull *x = k; x < k + l; ++x) {
int o = x[l] % Mod;
x[l] = 1ll * (*x + Mod - o) **g % Mod, *x += o;
}
}
}
int iv = pow(lim, Mod - 2);
for (int i = 0; i < lim; ++i) A[i] = 1ll * tmp[i] % Mod * iv % Mod;
std::reverse(A + 1, A + lim);
}
void DIF(int *A, int lim) {
for (int i = 0; i < lim; ++i) tmp[i] = A[i];
for (int l = lim / 2; l >= 1; l >>= 1) {
ull *k = tmp;
for (int *g = gw; k < tmp + lim; k += (l << 1), ++g) {
for (ull *x = k; x < k + l; ++x) {
int o = 1ll * x[l] **g % Mod;
x[l] = *x + Mod - o, *x += o;
}
}
}
for (int i = 0; i < lim; ++i) A[i] = tmp[i] % Mod;
}
Poly mult(const Poly &A, int n, const Poly &B, int m) {
if (n + m < 255) {
Poly ans(n + m - 1);
std::fill(tmp, tmp + n + m, 0);
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j) tmp[i + j] += 1ll * A[i] * B[j] % Mod;
for (int i = 0; i < n + m - 1; ++i) ans[i] = tmp[i] % Mod;
return ans;
}
int lim = 1;
while (lim < (n + m - 1)) lim <<= 1;
static int tA[N], tB[N];
std::copy_n(A.begin(), n, tA), std::fill(tA + n, tA + lim, 0);
std::copy_n(B.begin(), m, tB), std::fill(tB + m, tB + lim, 0);
DIF(tA, lim), DIF(tB, lim);
for (int i = 0; i < lim; ++i) tA[i] = 1ll * tA[i] * tB[i] % Mod;
DIT(tA, lim);
Poly ans(n + m - 1);
std::copy_n(tA, n + m - 1, ans.begin());
return ans;
}
Poly Tmul(const Poly &A, int n, const Poly &B, int m) {
if (n + m < 255) {
Poly ans(m - n + 1);
std::fill(tmp, tmp + m - n + 2, 0);
for (int i = 0; i < m; ++i)
for (int j = i; j < n; ++j) tmp[j - i] += 1ll * B[i] * A[j] % Mod;
for (int i = 0; i < m - n + 1; ++i) ans[i] = tmp[i] % Mod;
}
int lim = 1;
while (lim < m) lim <<= 1;
static int tA[N], tB[N];
std::reverse_copy(A.begin(), A.begin() + n, tA), std::fill(tA + n, tA + lim, 0);
std::copy_n(B.begin(), m, tB), std::fill(tB + m, tB + lim, 0);
DIF(tA, lim), DIF(tB, lim);
for (int i = 0; i < lim; ++i) tA[i] = 1ll * tA[i] * tB[i] % Mod;
DIT(tA, lim);
Poly ans(m - n + 1);
std::copy_n(tA + n - 1, m - n + 1, ans.begin());
return ans;
}
Poly inv(Poly A, int n) {
int lim = 1;
while (lim < (n << 1)) lim <<= 1;
Poly F(lim), G(lim);
A.resize(lim);
G[0] = pow(A[0], Mod - 2);
int now = 1;
static int tA[N], tB[N];
while (now < n) {
std::copy_n(A.begin(), now << 1, F.begin());
int lim = now << 2;
std::copy_n(G.begin(), lim, tA);
std::copy_n(F.begin(), lim, tB);
DIF(tA, lim), DIF(tB, lim);
for (int i = 0; i < lim; ++i) tA[i] = 1ll * sub(2, 1ll * tA[i] * tB[i] % Mod) * tA[i] % Mod;
DIT(tA, lim);
std::copy_n(tA, now << 1, G.begin());
now <<= 1;
}
G.resize(n);
return G;
}
Poly drv(const Poly &A, int n) {
Poly ans(n - 1);
for (int i = 0; i < n - 1; ++i) ans[i] = 1ll * A[i + 1] * (i + 1) % Mod;
return ans;
}
Poly itg(const Poly &A, int n) {
Poly ans(n + 1);
for (int i = 0; i < n; ++i) ans[i + 1] = 1ll * A[i] * iv[i + 1] % Mod;
return ans;
}
Poly ln(const Poly &A, int n) {
Poly F = drv(A, n), G = inv(A, n);
F = mult(F, n - 1, G, n);
F.resize(n - 1);
F = itg(F, n - 1);
return F;
}
Poly exp(Poly A, int n) {
int lim = 1;
while (lim < (n << 1)) lim <<= 1;
A.resize(lim);
Poly L(lim);
int now = 1;
static int tF[N], tG[N], tL[N];
std::fill(tG, tG + lim, 0), std::fill(tF, tF + lim, 0);
tG[0] = 1;
while (now < n) {
int lim = now << 2;
std::copy_n(tG, now, L.begin());
L = ln(L, std::min(now << 1, n));
L.resize(lim);
std::copy_n(A.begin(), now << 1, tF);
std::copy_n(L.begin(), lim, tL);
DIF(tF, lim), DIF(tG, lim), DIF(tL, lim);
for (int i = 0; i < lim; ++i) tG[i] = 1ll * tG[i] * sub(add(1, tF[i]), tL[i]) % Mod;
DIT(tG, lim);
std::fill(tG + (now << 1), tG + lim, 0);
now <<= 1;
}
Poly G(n);
std::copy_n(tG, n, G.begin());
return G;
}
void getg(int x, int xl, int xr, const Poly &f, int m) {
if (xl == xr) {
G[x].resize(2);
G[x][0] = 1;
if (xl >= m)
G[x][1] = 0;
else
G[x][1] = Mod - f[xl];
return;
}
int xm = (xl + xr) >> 1;
getg(x << 1, xl, xm, f, m), getg(x << 1 | 1, xm + 1, xr, f, m);
G[x] = mult(G[x << 1], xm - xl + 2, G[x << 1 | 1], xr - xm + 1);
}
void getans(int x, int xl, int xr, Poly &ans, int m, const Poly &h) {
if (xl >= m) return;
if (xl == xr) return void(ans[xl] = h[0]);
int xm = (xl + xr) >> 1;
Poly hl = Tmul(G[x << 1 | 1], xr - xm + 1, h, xr - xl + 1);
getans(x << 1, xl, xm, ans, m, hl);
Poly hr = Tmul(G[x << 1], xm - xl + 2, h, xr - xl + 1);
getans(x << 1 | 1, xm + 1, xr, ans, m, hr);
}
Poly getv(Poly A, int n, const std::vector<int> &f, int m) {
n = std::max(n, m);
A.resize(n);
getg(1, 0, n - 1, f, m);
Poly now = inv(G[1], n);
std::reverse(now.begin(), now.begin() + n);
Poly h = mult(now, n, A, n);
for (int i = 0; i < n; ++i) h[i] = h[i + n - 1];
h.resize(n);
Poly ans(m);
getans(1, 0, n - 1, ans, m, h);
return ans;
}
void init_Poly(int n) {
int t = 1;
while ((1 << t) < n) ++t;
t = std::min(t - 1, 21);
gw[0] = 1, gw[1 << t] = pow(31, 1 << (21 - t));
for (int i = t; i; --i) gw[1 << (i - 1)] = 1ll * gw[1 << i] * gw[1 << i] % Mod;
for (int i = 1; i < (1 << t); ++i) gw[i] = 1ll * gw[i & (i - 1)] * gw[i & -i] % Mod;
--n;
fac[0] = 1;
for (int i = 1; i <= n; ++i) fac[i] = 1ll * fac[i - 1] * i % Mod;
ifac[n] = Pol::pow(fac[n], Mod - 2);
for (int i = n - 1; i >= 0; --i) ifac[i] = 1ll * ifac[i + 1] * (i + 1) % Mod;
for (int i = 1; i <= n; ++i) iv[i] = 1ll * ifac[i] * fac[i - 1] % Mod;
}
int pow(int a, int b, int ans) {
while (b) {
if (b & 1) ans = 1ll * ans * a % Mod;
a = 1ll * a * a % Mod;
b >>= 1;
}
return ans;
}
} // namespace Pol