多项式乘法(NTT)
默认大家会了FFT……
我们知道FFT中因为要用三角函数和复数,所以对精度要求高
并且常数比较大
那么有没有可以代替单位根的呢?
我们思考一下在FFT中应用的单位根的性质
1:
w
n
0
=
1
w_n^0=1
wn0=1
保证了有初始值
2:
w
n
i
+
k
=
w
n
i
w
n
j
w_n^{i+k}=w_n^iw_n^j
wni+k=wniwnj,也就是
w
n
k
=
(
w
n
1
)
k
w_n^k=(w_n^1)^k
wnk=(wn1)k
因此只要求出一个就可以递推
3:
w
n
k
+
n
/
2
=
−
w
n
k
w_n^{k+n/2}=-w_{n}^k
wnk+n/2=−wnk
保证了计算的简洁性
4:
w
n
0
,
1
…
…
n
−
1
w_n^{0,1……n-1}
wn0,1……n−1互不相同
保证了代入了不同的数
5:
(
w
2
n
1
)
2
=
w
n
1
(w_{2n}^1)^2=w_n^1
(w2n1)2=wn1
保证了计算式可以分治
前人们发现原根
g
g
g也有相似的优良性质
简单介绍原根和阶
对于一个质数
p
p
p,和一个数a,a在mod p意义下的阶就是
a
r
≡
1
(
m
o
d
p
)
a^r\equiv1(mod\;p)
ar≡1(modp)
的最小整数
r
r
r
一个重要的性质是
a
0
,
a
1
,
a
2
…
…
,
a
r
−
1
a^0,a^1,a^2……,a^{r-1}
a0,a1,a2……,ar−1互不相同
证明的话假设有
a
x
≡
a
y
(
m
o
d
p
)
a^x\equiv a^y(mod\;p)
ax≡ay(modp)
那么
a
∣
x
−
y
∣
≡
1
(
m
o
d
p
)
a^{|x-y|} \equiv 1(mod\;p)
a∣x−y∣≡1(modp)
显然
∣
x
−
y
∣
<
r
|x-y|<r
∣x−y∣<r,但是根据阶的定义,r是最小的满足这个式子的数,矛盾
那么是原根呢?
一个质数的原根g是满足下列条件的数的集合
g g g在模p意义下的阶是 p − 1 p-1 p−1
当然一个质数不只有一个原根,但总有一个最小原根
根据前人的总结,发现若定义
g
n
1
=
g
(
p
−
1
)
n
g_n^1=g^{\frac{(p-1)}{n}}
gn1=gn(p−1)
那么这个东西有和
w
n
1
w_n^1
wn1相似的性质
我们一一来看
1:
g
n
0
=
(
g
(
p
−
1
)
n
)
0
=
1
g_n^0=(g^{\frac{(p-1)}{n}})^0=1
gn0=(gn(p−1))0=1
满足
2: g n i + j = ( g ( p − 1 ) n ) i + j = ( g ( p − 1 ) n ) i ( g ( p − 1 ) n ) j = g n i g n j g_n^{i+j}=(g^{\frac{(p-1)}{n}})^{i+j}=(g^{\frac{(p-1)}{n}})^i(g^{\frac{(p-1)}{n}})^j=g_{n}^ig_n^j gni+j=(gn(p−1))i+j=(gn(p−1))i(gn(p−1))j=gnignj
3:
g
n
k
+
n
/
2
=
(
g
(
p
−
1
)
n
)
k
(
g
(
p
−
1
)
n
)
n
/
2
=
g
k
(
p
−
1
)
n
g
(
p
−
1
)
2
g_n^{k+n/2}=(g^{\frac{(p-1)}{n}})^k(g^{\frac{(p-1)}{n}})^{n/2}=g^{\frac{k(p-1)}{n}}g^{\frac{(p-1)}{2}}
gnk+n/2=(gn(p−1))k(gn(p−1))n/2=gnk(p−1)g2(p−1)
而我们知道
g
p
−
1
≡
1
g^{p-1}\equiv 1
gp−1≡1,因此
g
(
p
−
1
)
2
≡
+
1
或
−
1
g^{\frac{(p-1)}{2}}\equiv +1或-1
g2(p−1)≡+1或−1
又因为上述阶的性质,所有g的次幂互不相同,因此
g
(
p
−
1
)
2
≡
−
1
g^{\frac{(p-1)}{2}}\equiv -1
g2(p−1)≡−1
代入上式
g
n
k
+
n
/
2
=
g
k
(
p
−
1
)
n
g
(
p
−
1
)
2
=
−
g
k
(
p
−
1
)
n
=
−
g
n
k
g_n^{k+n/2}=g^{\frac{k(p-1)}{n}}g^{\frac{(p-1)}{2}}=-g^{\frac{k(p-1)}{n}}=-g_n^k
gnk+n/2=gnk(p−1)g2(p−1)=−gnk(p−1)=−gnk
4:由阶的性质可得
5:
(
g
2
n
1
)
2
=
(
g
(
p
−
1
)
2
n
)
2
=
g
(
p
−
1
)
n
=
g
n
1
(g_{2n}^1)^2=(g^{\frac{(p-1)}{2n}})^2=g^{\frac{(p-1)}{n}}=g_n^1
(g2n1)2=(g2n(p−1))2=gn(p−1)=gn1
至此我们发现w需要的性质g都有
问题解决啦
……
吗?
上述式子中有一个问题就是
g
(
p
−
1
)
n
g^{\frac{(p-1)}{n}}
gn(p−1)中
(
p
−
1
)
(p-1)
(p−1)可能不是n的倍数
如果您了解FFT,就会知道这里的
n
n
n是2的整次幂
因此,一个模数合法要求(p-1)的因子中含有大量2的幂
这样的模数不算特别多,一个最常见的就是998244353
因此,我们只需要代入模数,就可以仿照FFT的方式啦
这就是NTT
void NTT(Poly &f,int opt,int n)
{
for(int i=0;i<n;i++)
if(i<tr[i]) swap(f[i],f[tr[i]]);
for(int len=2;len<=n;len<<=1)
{
int l=len/2;
int w=Pow(opt?G:IG,(mod-1)/len);
for(int i=0;i<n;i+=len)
{
LL g=1;
for(int k=i;k<i+l;k++)
{
LL v=g*f[k+l]%mod;
f[k+l]=(f[k]-v+mod)%mod;
f[k]=(f[k]+v)%mod;
g=1ll*g*w%mod;
}
}
}
}
然后是多项式乘法,和FFT类似的
#define Poly vector<int>
#define len(x) (int)x.size()
#define turn(x,n) x.resize(n)
void Retry(int n)
{
for(int i=0;i<n;i++)
tr[i]=((tr[i>>1]>>1)|((i&1)?(n>>1):0));
}
Poly Mul(Poly f,Poly g)
{
int n=max(len(f),len(g));
turn(f,n);
for(int i=0;i<n;i++)
f[i]=1ll*f[i]*g[i]%mod;
return f;
}
Poly Times(Poly f,Poly g)
{
int n=len(f),m=len(g);
for(m+=n-1,n=1;n<m;n<<=1);
LL invn=Inv(n);
turn(f,n);turn(g,n);
Retry(n);
NTT(f,1,n);NTT(g,1,n);
f=Mul(f,g);
NTT(f,0,n);f=Mul(f,invn);
turn(f,min(m,mxlen));
return f;
}
多项式求逆
已知多项式
F
(
x
)
F(x)
F(x),求
G
(
x
)
G(x)
G(x)满足
G
(
x
)
F
(
x
)
≡
1
(
m
o
d
98244353
)
G(x)F(x)\equiv 1(mod\; 98244353)
G(x)F(x)≡1(mod98244353)
由于题目良心,所以模数正好是NTT模数
我们考虑用倍增的方法求
下文都用
F
∗
(
x
)
F_*(x)
F∗(x)表示次数是
F
(
x
)
F(x)
F(x)一半的多项式,其余一样
那么我们假设已经倍增的求出
G
∗
(
x
)
G_*(x)
G∗(x),要求
G
(
x
)
G(x)
G(x)
那么显然
G
(
x
)
≡
G
∗
(
x
)
(
m
o
d
x
n
/
2
)
G(x)\equiv G_*(x)(mod\;x^{n/2})
G(x)≡G∗(x)(modxn/2)
移项
G
(
x
)
−
G
∗
(
x
)
≡
0
(
m
o
d
x
n
/
2
)
G(x)-G_*(x)\equiv 0(mod\;x^{n/2})
G(x)−G∗(x)≡0(modxn/2)
两边同时平方
(
G
(
x
)
−
G
∗
(
x
)
)
2
≡
0
(
m
o
d
x
n
)
(G(x)-G_*(x))^2\equiv 0(mod\;x^n)
(G(x)−G∗(x))2≡0(modxn)
G
(
x
)
2
−
2
G
(
x
)
G
∗
(
x
)
+
G
∗
(
x
)
2
≡
0
(
m
o
d
x
n
)
G(x)^2-2G(x)G_*(x)+G_*(x)^2\equiv 0(mod\;x^n)
G(x)2−2G(x)G∗(x)+G∗(x)2≡0(modxn)
同时乘上
F
(
x
)
F(x)
F(x)
G
(
x
)
−
2
G
∗
(
x
)
+
G
∗
(
x
)
2
F
(
x
)
≡
0
(
m
o
d
x
n
)
G(x)-2G_*(x)+G_*(x)^2F(x)\equiv 0(mod\;x^n)
G(x)−2G∗(x)+G∗(x)2F(x)≡0(modxn)
再次移项
G
(
x
)
≡
2
G
∗
(
x
)
−
G
∗
(
x
)
2
F
(
x
)
(
m
o
d
x
n
)
G(x)\equiv 2G_*(x)-G_*(x)^2F(x)(mod\;x^n)
G(x)≡2G∗(x)−G∗(x)2F(x)(modxn)
G
(
x
)
≡
G
∗
(
x
)
(
2
−
G
∗
(
x
)
F
(
x
)
)
(
m
o
d
x
n
)
G(x)\equiv G_*(x)(2-G_*(x)F(x))(mod\;x^n)
G(x)≡G∗(x)(2−G∗(x)F(x))(modxn)
倍增计算,复杂度
O
(
n
log
n
)
O(n\log n)
O(nlogn)
Poly Inv(Poly f)
{
Poly g=Poly(1,Inv(f[0]));
for(mxlen=2;mxlen<2*len(f);mxlen<<=1)
{
Poly h=f;
turn(h,mxlen);
g=Times(g,Sub(2,Times(g,h)));
}
turn(g,len(f));
return g;
}
多项式求导
考虑
已知
F
(
x
)
F(x)
F(x),求导数
F
′
(
x
)
F'(x)
F′(x)
F
(
x
+
d
x
)
=
∑
i
=
0
n
f
i
(
x
+
d
x
)
i
F(x+dx)=\sum_{i=0}^nf_i(x+dx)^i
F(x+dx)=i=0∑nfi(x+dx)i
代入二项式定理
F
(
x
+
d
x
)
=
∑
i
=
0
n
f
i
(
C
i
0
x
i
+
C
i
1
x
i
−
1
d
x
+
C
i
2
x
i
−
2
d
x
2
…
…
)
F(x+dx)=\sum_{i=0}^nf_i(C_i^0x^i+C_i^1x^{i-1}dx+C_i^2x^{i-2}dx^2……)
F(x+dx)=i=0∑nfi(Ci0xi+Ci1xi−1dx+Ci2xi−2dx2……)
F
(
x
+
d
x
)
−
F
(
x
)
=
∑
i
=
0
n
f
i
(
C
i
1
x
i
−
1
d
x
+
C
i
2
x
i
−
2
d
x
2
…
…
)
F(x+dx)-F(x)=\sum_{i=0}^nf_i(C_i^1x^{i-1}dx+C_i^2x^{i-2}dx^2……)
F(x+dx)−F(x)=i=0∑nfi(Ci1xi−1dx+Ci2xi−2dx2……)
代入导数定义式
F
′
(
x
)
=
F
(
x
+
d
x
)
−
F
(
x
)
d
x
=
∑
i
=
0
n
f
i
i
x
i
−
1
F'(x)=\frac{F(x+dx)-F(x)}{dx}=\sum_{i=0}^nf_iix^{i-1}
F′(x)=dxF(x+dx)−F(x)=i=0∑nfiixi−1
也就是
F
′
(
x
)
=
∑
i
=
0
n
−
1
f
i
+
1
(
i
+
1
)
x
i
F'(x)=\sum_{i=0}^{n-1}f_{i+1}(i+1)x^i
F′(x)=i=0∑n−1fi+1(i+1)xi
Poly Der(Poly f)
{
for(int i=0;i<len(f)-1;i++)
f[i]=Mul(i+1,f[i+1]);
turn(f,len(f)-1);
return f;
}
多项式积分
把求导逆过来就好了
Poly Int(Poly f)
{
turn(f,len(f)+1);
for(int i=len(f)-1;i>=1;i--)
f[i]=Mul(f[i-1],inv[i]);
f[0]=0;
return f;
}
多项式牛顿迭代
已知函数
F
(
x
)
F(x)
F(x),求函数
G
(
x
)
G(x)
G(x)使得
F
(
G
(
x
)
)
=
0
F(G(x))=0
F(G(x))=0
直接给出公式吧
G
(
x
)
=
G
∗
(
x
)
−
F
(
G
∗
(
x
)
)
F
′
(
G
∗
(
x
)
)
G(x)=G_*(x)-\frac{F(G_*(x))}{F'(G_*(x))}
G(x)=G∗(x)−F′(G∗(x))F(G∗(x))
多项式ln
已知函数
F
(
x
)
F(x)
F(x),求
G
(
x
)
=
l
n
(
F
(
x
)
)
G(x)=ln(F(x))
G(x)=ln(F(x))
G
(
x
)
=
l
n
(
F
(
x
)
)
G(x)=ln(F(x))
G(x)=ln(F(x))
两遍同时求导
G
′
(
x
)
=
(
l
n
(
F
(
x
)
)
)
′
G'(x)=(ln(F(x)))'
G′(x)=(ln(F(x)))′
根据复合函数求导的链式法则
(
F
(
G
(
x
)
)
)
′
=
F
′
(
G
(
x
)
)
G
′
(
x
)
(F(G(x)))'=F'(G(x))G'(x)
(F(G(x)))′=F′(G(x))G′(x)
而我们知道
l
n
′
(
x
)
=
1
x
ln'(x)=\frac{1}{x}
ln′(x)=x1
右边的式子也就是
G
′
(
x
)
=
F
′
(
x
)
F
(
x
)
G'(x)=\frac{F'(x)}{F(x)}
G′(x)=F(x)F′(x)
这个时候就可以算了
然后再积分回去就可以得到原函数了
Poly Ln(Poly f)
{
return Int(Times(Der(f),Inv(f)));
}
多项式exp
已知函数
F
(
x
)
F(x)
F(x),求
G
(
x
)
=
e
F
(
x
)
G(x)=e^{F(x)}
G(x)=eF(x)
两边同时取
l
n
ln
ln
l
n
G
(
x
)
=
F
(
x
)
lnG(x)=F(x)
lnG(x)=F(x)
移项得
l
n
G
(
x
)
−
F
(
x
)
=
0
lnG(x)-F(x)=0
lnG(x)−F(x)=0
设
H
(
G
(
x
)
)
=
l
n
G
(
x
)
−
F
(
x
)
H(G(x))=lnG(x)-F(x)
H(G(x))=lnG(x)−F(x)
也就是
H
(
G
(
x
)
)
=
0
H(G(x))=0
H(G(x))=0
很符合牛顿迭代的式子
代入公式
G
(
x
)
=
G
∗
(
x
)
−
H
(
G
∗
(
x
)
)
H
′
(
G
∗
(
x
)
)
G(x)=G_*(x)-\frac{H(G_*(x))}{H'(G_*(x))}
G(x)=G∗(x)−H′(G∗(x))H(G∗(x))
因为
F
(
x
)
F(x)
F(x)是常数,所以不会对导数有贡献
H
′
(
G
(
x
)
)
=
1
G
(
x
)
H'(G(x))=\frac{1}{G(x)}
H′(G(x))=G(x)1
回带
G
(
x
)
=
G
∗
(
x
)
−
G
∗
(
x
)
H
(
G
∗
(
x
)
)
G(x)=G_*(x)-G_*(x)H(G_*(x))
G(x)=G∗(x)−G∗(x)H(G∗(x))
G
(
x
)
=
G
∗
(
x
)
−
G
∗
(
x
)
(
l
n
(
G
∗
(
x
)
)
−
F
(
x
)
)
G(x)=G_*(x)-G_*(x)(ln(G_*(x))-F(x))
G(x)=G∗(x)−G∗(x)(ln(G∗(x))−F(x))
G
(
x
)
=
G
∗
(
x
)
(
1
−
l
n
(
G
∗
(
x
)
)
+
F
(
x
)
)
G(x)=G_*(x)(1-ln(G_*(x))+F(x))
G(x)=G∗(x)(1−ln(G∗(x))+F(x))
倍增求解就行啦
Poly Exp(Poly f)
{
Poly g=Poly(1,1);
for(mxlen=2;mxlen<4*len(f);mxlen<<=1)
{
Poly A=f;
turn(A,mxlen);
g=Times(g,Add(Sub(1,Ln(g)),A));
}
turn(g,len(f));
return g;
}
多项式快速幂
已知多项式
F
(
x
)
F(x)
F(x),求
G
(
x
)
=
F
(
x
)
k
G(x)=F(x)^k
G(x)=F(x)k
我们知道
e
l
n
(
x
)
=
x
e^{ln(x)}=x
eln(x)=x
所以
x
k
=
(
e
l
n
(
x
)
)
k
x^k=(e^{ln(x)})^k
xk=(eln(x))k
x
k
=
e
k
l
n
(
x
)
)
x^k=e^{kln(x)})
xk=ekln(x))
我们先对
F
(
x
)
F(x)
F(x)取
l
n
ln
ln,然后乘以k,再exp回去就行啦
这里我们也可以知道这个k完全可以是mod p同余的,因此即使指数很大也没事啦
Poly Pow(Poly f,int k)
{
f=Ln(f);
f=Mul(f,k);
f=Exp(f);
return f;
}
多项式开根
已知函数
F
(
x
)
F(x)
F(x),求
G
(
x
)
2
=
F
(
x
)
G(x)^2=F(x)
G(x)2=F(x)
方法一:调用多项式快速幂,不过常数巨大
方法二:使用牛顿迭代
因为
G
(
x
)
2
=
F
(
x
)
G(x)^2=F(x)
G(x)2=F(x)
G
(
x
)
2
−
F
(
x
)
=
0
G(x)^2-F(x)=0
G(x)2−F(x)=0
设
H
(
G
(
x
)
)
=
G
(
x
)
2
−
F
(
x
)
H(G(x))=G(x)^2-F(x)
H(G(x))=G(x)2−F(x)
求导得
H
′
(
G
(
x
)
)
=
2
G
(
x
)
H'(G(x))=2G(x)
H′(G(x))=2G(x)
代入牛顿迭代
G
(
x
)
=
G
∗
(
x
)
−
H
(
G
∗
(
x
)
)
H
′
(
G
∗
(
x
)
)
G(x)=G_*(x)-\frac{H(G_*(x))}{H'(G_*(x))}
G(x)=G∗(x)−H′(G∗(x))H(G∗(x))
G
(
x
)
=
G
∗
(
x
)
−
G
∗
(
x
)
2
−
F
(
x
)
2
G
∗
(
x
)
G(x)=G_*(x)-\frac{G_*(x)^2-F(x)}{2G_*(x)}
G(x)=G∗(x)−2G∗(x)G∗(x)2−F(x)
G
(
x
)
=
G
∗
(
x
)
2
+
F
(
x
)
2
G
∗
(
x
)
G(x)=\frac{G_*(x)^2+F(x)}{2G_*(x)}
G(x)=2G∗(x)G∗(x)2+F(x)
倍增求解即可
Poly Sqrt(Poly f)
{
Poly g=Poly(1,1);
for(mxlen=2;mxlen<4*len(f);mxlen<<=1)
{
Poly A=f;turn(A,mxlen);
g=Times(Add(Times(g,g),A),Mul(Inv(g),inv[2]));
}
turn(g,len(f));
return g;
}
多项式带余除法
已知
F
(
x
)
F(x)
F(x),
G
(
x
)
G(x)
G(x),求
H
(
x
)
,
R
(
x
)
H(x),R(x)
H(x),R(x)满足,
F
(
x
)
=
H
(
x
)
G
(
x
)
+
R
(
x
)
F(x)=H(x)G(x)+R(x)
F(x)=H(x)G(x)+R(x)
设
F
r
(
x
)
=
x
n
F
(
1
x
)
F_r(x)=x^nF(\frac{1}{x})
Fr(x)=xnF(x1)
我们看这个
F
r
(
x
)
F_r(x)
Fr(x)究竟是什么
F
r
(
x
)
=
x
n
F
(
x
−
1
)
=
∑
i
=
0
n
f
i
x
n
x
−
i
=
∑
i
=
0
n
f
i
x
n
−
i
=
∑
i
=
0
n
f
n
−
i
x
i
F_r(x)=x^nF(x^{-1})=\sum_{i=0}^nf_ix^nx^{-i}=\sum_{i=0}^nf_ix^{n-i}=\sum_{i=0}^nf_{n-i}x^{i}
Fr(x)=xnF(x−1)=i=0∑nfixnx−i=i=0∑nfixn−i=i=0∑nfn−ixi
也就是将
F
(
x
)
F(x)
F(x)系数翻转
我们观察式子
F
(
x
)
=
H
(
x
)
G
(
x
)
+
R
(
x
)
F(x)=H(x)G(x)+R(x)
F(x)=H(x)G(x)+R(x)
带入
1
x
\frac{1}{x}
x1
F
(
1
x
)
=
H
(
1
x
)
G
(
1
x
)
+
R
(
1
x
)
F(\frac{1}{x})=H(\frac{1}{x})G(\frac{1}{x})+R(\frac{1}{x})
F(x1)=H(x1)G(x1)+R(x1)
同乘
x
n
x^n
xn
x
n
F
(
1
x
)
=
x
n
−
m
H
(
1
x
)
x
m
G
(
1
x
)
+
x
n
−
m
+
1
x
m
−
1
R
(
1
x
)
x^nF(\frac{1}{x})=x^{n-m}H(\frac{1}{x})x^mG(\frac{1}{x})+x^{n-m+1}x^{m-1}R(\frac{1}{x})
xnF(x1)=xn−mH(x1)xmG(x1)+xn−m+1xm−1R(x1)
F
r
(
x
)
=
H
r
(
x
)
G
r
(
x
)
+
x
n
−
m
+
1
R
r
(
x
)
F_r(x)=H_r(x)G_r(x)+x^{n-m+1}R_r(x)
Fr(x)=Hr(x)Gr(x)+xn−m+1Rr(x)
同模
x
n
+
m
−
1
x^{n+m-1}
xn+m−1
F
r
(
x
)
≡
H
r
(
x
)
G
r
(
x
)
F_r(x)\equiv H_r(x)G_r(x)
Fr(x)≡Hr(x)Gr(x)
H
r
(
x
)
≡
G
r
(
x
)
F
r
(
x
)
H_r(x)\equiv\frac{G_r(x)}{F_r(x)}
Hr(x)≡Fr(x)Gr(x)
多项式求逆算一下,再翻转系数
Poly Divide(Poly f,Poly g,int n,int m)
{
Poly Tf,Tg;
turn(Tf,n+1);
turn(Tg,n+1);
for(int i=0;i<=n;i++)
Tf[n-i]=f[i];
for(int i=0;i<=m;i++)
Tg[m-i]=g[i];
for(int i=n-m+2;i<=m;i++) Tg[i]=0;
Tg=Inv(Tg);
Tf=Times(Tf,Tg);
Reverse(Tf,n-m+1);
for(int i=n-m+1;i<=n;i++)
Tf[i]=0;
return Tf;
}
R ( x ) = F ( x ) − G ( x ) H ( x ) R(x)=F(x)-G(x)H(x) R(x)=F(x)−G(x)H(x)
Poly Mod(Poly f,Poly g,int n,int m)
{
Poly h=Divide(f,g,n,m);
Poly res=Sub(f,Times(g,h));
return res;
}
完整代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define int long long
const int mod = 998244353;
const int N = 1e6+7;
int Add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
int Sub(int x,int y){return x-y<0?x-y+mod:x-y;}
int Mul(int x,int y){return 1ll*x*y%mod;}
int Pow(int a,int b)
{
int res=1;
while(b)
{
if(b&1) res=1ll*res*a%mod;
a=1ll*a*a%mod;
b>>=1;
}
return res;
}
int Inv(int x){return Pow(x,mod-2);}
int inv[N];
void Init()
{
inv[1]=1;
for(int i=2;i<N;i++)
inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
}
#define Poly vector<int>
#define len(x) (int)x.size()
#define turn(x,n) x.resize(n)
Poly Sub(int x,Poly f)
{
for(int i=0;i<len(f);i++)
f[i]=mod-f[i];
f[0]=Add(f[0],x);
return f;
}
Poly Add(Poly f,int x)
{
f[0]=Add(f[0],x);
return f;
}
Poly Sub(Poly f,int x)
{
f[0]=Sub(f[0],x);
return f;
}
Poly Mul(Poly f,int x)
{
for(int i=0;i<len(f);i++)
f[i]=Mul(f[i],x);
return f;
}
Poly Add(Poly f,Poly g)
{
int n=max(len(f),len(g));
turn(f,n);
for(int i=0;i<n;i++)
f[i]=Add(f[i],g[i]);
return f;
}
Poly Sub(Poly f,Poly g)
{
int n=max(len(f),len(g));
turn(f,n);
for(int i=0;i<n;i++)
f[i]=Sub(f[i],g[i]);
return f;
}
const int G=3,IG=Pow(G,mod-2);
int tr[N];
void Retry(int n)
{
for(int i=0;i<n;i++)
tr[i]=((tr[i>>1]>>1)|((i&1)?(n>>1):0));
}
void NTT(Poly &f,int opt,int n)
{
for(int i=0;i<n;i++)
if(i<tr[i]) swap(f[i],f[tr[i]]);
for(int len=2;len<=n;len<<=1)
{
int l=len/2;
int w=Pow(opt?G:IG,(mod-1)/len);
for(int i=0;i<n;i+=len)
{
LL g=1;
for(int k=i;k<i+l;k++)
{
LL v=g*f[k+l]%mod;
f[k+l]=(f[k]-v+mod)%mod;
f[k]=(f[k]+v)%mod;
g=1ll*g*w%mod;
}
}
}
}
Poly Mul(Poly f,Poly g)
{
int n=max(len(f),len(g));
turn(f,n);
for(int i=0;i<n;i++)
f[i]=1ll*f[i]*g[i]%mod;
return f;
}
int mxlen=100000000;
Poly Times(Poly f,Poly g)
{
int n=len(f),m=len(g);
for(m+=n-1,n=1;n<m;n<<=1);
LL invn=Inv(n);
turn(f,n);turn(g,n);
Retry(n);
NTT(f,1,n);NTT(g,1,n);
f=Mul(f,g);
NTT(f,0,n);f=Mul(f,invn);
turn(f,min(m,mxlen));
return f;
}
Poly Inv(Poly f)
{
Poly g=Poly(1,Inv(f[0]));
for(mxlen=2;mxlen<2*len(f);mxlen<<=1)
{
Poly h=f;
turn(h,mxlen);
g=Times(g,Sub(2,Times(g,h)));
}
turn(g,len(f));
return g;
}
Poly Der(Poly f)
{
for(int i=0;i<len(f)-1;i++)
f[i]=Mul(i+1,f[i+1]);
turn(f,len(f)-1);
return f;
}
Poly Int(Poly f)
{
turn(f,len(f)+1);
for(int i=len(f)-1;i>=1;i--)
f[i]=Mul(f[i-1],inv[i]);
f[0]=0;
return f;
}
Poly Ln(Poly f)
{
return Int(Times(Der(f),Inv(f)));
}
Poly Exp(Poly f)
{
Poly g=Poly(1,1);
for(mxlen=2;mxlen<4*len(f);mxlen<<=1)
{
Poly A=f;
turn(A,mxlen);
g=Times(g,Add(Sub(1,Ln(g)),A));
}
turn(g,len(f));
return g;
}
Poly Pow(Poly f,int k)
{
f=Ln(f);
f=Mul(f,k);
f=Exp(f);
return f;
}
Poly Sqrt(Poly f)
{
Poly g=Poly(1,1);
for(mxlen=2;mxlen<4*len(f);mxlen<<=1)
{
Poly A=f;turn(A,mxlen);
g=Times(Add(Times(g,g),A),Mul(Inv(g),2));
}
turn(g,len(f));
return g;
}
void Put(Poly f,int n)
{
for(int i=0;i<n;i++)
printf("%lld ",f[i]);
printf("\n");
}
char s[N];
LL Get()
{
LL num=0;
scanf("%s",s+1);
LL m=strlen(s+1);
for(int i=1;i<=m;i++)
{
num=num*10ll%mod;
num=(num+s[i]-'0')%mod;
}
return num;
}
void Reverse(Poly &f,int len)
{
Poly g=f;
for(int i=0;i<len;i++)
f[i]=g[len-1-i];
}
Poly Divide(Poly f,Poly g,int n,int m)
{
Poly Tf,Tg;
turn(Tf,n+1);
turn(Tg,n+1);
for(int i=0;i<=n;i++)
Tf[n-i]=f[i];
for(int i=0;i<=m;i++)
Tg[m-i]=g[i];
for(int i=n-m+2;i<=m;i++) Tg[i]=0;
Tg=Inv(Tg);
Tf=Times(Tf,Tg);
Reverse(Tf,n-m+1);
for(int i=n-m+1;i<=n;i++)
Tf[i]=0;
return Tf;
}
Poly Mod(Poly f,Poly g,int n,int m)
{
Poly h=Divide(f,g,n,m);
Poly res=Sub(f,Times(g,h));
return res;
}
signed main()
{
return 0;
}