(首先要%miskcoo,这位dalao写的博客实在是太强啦qwq大部分多项式相关的知识都是从这位dalao博客里面学的,做为一只蒟蒻仍是疯狂膜拜后本身理下思路吧qwq)函数
对于一个多项式\(A(x)\),若是存在一个多项式\(B(x)\),知足\(B(x)\)的次数小于等于\(A(x)\)且\(A(x)B(x)\equiv 1(mod\ x^n)\),那么咱们称\(B(x)\)为\(A(x)\)在模\(x^n\)意义下的逆元,简单记做\(A^{-1}(x)\)ui
从最简单的状况开始考虑,当\(n=1\)的时候\(A(x)\equiv\ c\ (mod\ x)\),\(c\)为\(A(x)\)的常数项,此时\(A^{-1}(x)\)为\(c\)的逆元spa
在这个基础上咱们继续考虑通常状况code
对于\(n>1\)的状况,不妨设\(B(x)=A^{-1}(x)\),那么咱们能够根据定义列出下面的式子:
\[ A(x)B(x)\equiv 1(mod\ x^n) \]
这里的话考虑用倍增的方式求解(算倍增吧),这里假设咱们已经知道了\(A(x)\)在\(mod\ x^{\lceil \frac{n}{2} \rceil}\)下的逆元\(G(x)\),那么有:
\[ A(x)G(x)\equiv 1(mod\ x^{\lceil \frac{n}{2} \rceil}) \]
咱们把\(A(x)\)和\(B(x)\)的式子写成\(mod\ x^{\lceil \frac{n}{2} \rceil}\)下的:blog
(能够这么写是由于\(mod\ x^n\)至关于将乘积中\(x\)次数大于等于\(n\)的忽略掉了,而\(mod\ x^{\lceil \frac{n}{2} \rceil}\)则至关于忽略了更多的项,既然前者知足,那么后者确定也知足)递归
\[ A(x)B(x)\equiv 1 (mod\ x^{\lceil \frac{n}{2} \rceil}) \]get
把这两条式子相减,就能够搞事情了:
\[ \begin{aligned} A(x)[B(x)-G(x)]&\equiv 0\ (mod\ x^{\lceil \frac{n}{2} \rceil})\\ B(x)-G(x)&\equiv 0\ (mod\ x^{\lceil \frac{n}{2} \rceil})\\ \end{aligned} \]
而后咱们两边平方一下:
\[ B^2(x)-2B(x)G(x)+G^2(x)\equiv 0\ (mod\ x^{\lceil \frac{n}{2} \rceil}) \]
而后这里有个很神奇的事情,\(B(x)-G(x)\) 在\(mod\ x^{\lceil \frac{n}{2} \rceil}\)下为0,说明这个式子最后的结果的\(0\)到\(\lceil \frac{n}{2} \rceil -1\)次项系数都为\(0\),平方了以后,对于结果的\(i\)次项系数,(\(0<=i<=2*\lceil \frac{n}{2} \rceil -1\) ),其系数\(a_i = \sum\limits_{j=0}^{i}a_j a_{i-j}\),而\(a_j\)和\(a_{i-j}\)中一定有一项为\(0\)(由于\(j\)和\(i-j\)中一定有一个值小于\(\lceil \frac{n}{2} \rceil\)),因此咱们能够获得一个结论,这个式子在平方了以后在\(mod \ x^n\)下也是\(0\)博客
那么咱们就能够写成:
\[ \begin{aligned} B^2(x)-2B(x)G(x)+G^2(x)&\equiv 0\ (mod\ x^n)\\ A(x)B(x)*B(x)-2*A(x)B(x)*G(x)+A(x)G^2(x)&\equiv 0\ (mod\ x^n)\\ \end{aligned} \]
两边同时乘上\(A(x)\),由逆元的定义咱们能够将上面的式子化简成下面这样:
\[ B(x)-2G(x)+A(x)G^2(x)\equiv 0\ (mod\ x^n) \]
最后获得:
\[ B(x)\equiv 2G(x)-A(x)G^2(x)\ (mod\ x^n) \]
也就是说,若是咱们知道\(G(x)\),咱们就能够推出\(B(x)\)了it
具体的实现能够用递归的方式实现,中间的多项式乘法能够用fft加速一下,那么最终的时间复杂度就是
\[ T(n)=T(\frac{n}{2})+O(n \ log\ n)=O(n\ log \ n) \]
然而为啥这样搞完了仍是一个log呢?由于每次递归下去多项式的最高次数都会减半,稍微算一下就会发现最后总的时间复杂度合起来仍是一个log而不是两个io
注意,后面这一堆推式子的过程是创建在\(n=1\)的时候有解的前提下的,因此咱们还能够获得一个结论:一个多项式在\(mod\ x^n\)下是否有逆元取决于其常数项在\(mod \ x^n\)下是否有逆元
首先先实现一个namespace NTT,而后除了基础的函数外主要供外部调用的过程是这个:
void Ntt_getinv(vct &a,vct &b,int n,int m){ prework(a,b,n,2*m);//这里注意由于后面是A*B*B,因此m要*2 ntt(A,1); ntt(B,1); for (int i=0;i<len;++i) B[i]=(2LL-1LL*A[i]*B[i]%MOD+MOD)*1LL*B[i]%MOD; ntt(B,-1); }
而后求逆的过程大概是这样(这里用vector来写了):
vct Inv(vct a){ int N=a.size(); if (N==1){ a[0]=ksm(a[0],MOD-2); return a; } vct b=a; b.resize((N+1)>>1); b=Inv(b); b.resize(N); NTT::Ntt_getinv(a,b,N,N); b.resize(NTT::len); for (int i=0;i<NTT::len;++i) b[i]=NTT::B[i]; b.resize(N); return b; }
求逆大概就是这样吧ovo
对于一个多项式\(A(x)\),若是存在一个多项式\(B(x)\),知足\(B^2(x)\equiv\ A(x) (mod\ x^n)\),则称\(B(x)\)为\(A(x)\)在\(mod\ x^n\)下的平方根
一样是考虑最简单的状况,当\(n=0\)的时候,\(B(x)\)的常数项就是\(1\)
而后考虑通常状况,一样的思路,考虑用倍增的方式来求
假设咱们已经知道了\(A(x)\)在\(mod\ x^{n}\)下的平方根\(G(x)\),如今要求在\(mod\ x^{2n}\)下的平方根\(B(x)\),根据定义咱们能够列出式子:
\[ \begin{aligned} B^2(x)&\equiv A(x)(mod\ x^{2n})\\ G^2(x)&\equiv A(x)(mod\ x^n) \end{aligned} \]
咱们对这个式子进行一些处理:
\[ \begin{aligned} G^2(x)&\equiv A(x)(mod\ x^n)\\ G^2(x)-A(x)&\equiv 0(mod\ x^n)\\ \end{aligned} \]
那么能够获得(由于右边是\(0\)因此能够这么搞):
\[ \begin{aligned} (G^2(x)-A(x))^2&\equiv 0 (mod\ x^{2n})\\ G^4(x)-2G^2(x)A(x)+A^2(x)&\equiv 0(mod\ x^{2n})\\ \end{aligned} \]
而后两边加上\(4G^2(x)A(x)\):
\[ \begin{aligned} G^4(x)+2G^2(x)A(x)+A^2(x)&\equiv 4G^2(x)A(x)(mod\ x^{2n})\\ (G^2(x)+A(x))^2&\equiv 4G^2(x)A(x)(mod\ x^{2n})\\ (G^2(x)+A(x))^2&\equiv (2G(x))^2A(x)(mod\ x^{2n})\\ \end{aligned} \]
咱们将\((2G^2(x))^2\)移到左边去,将左边写成一个平方的形式,获得:
\[ (\frac{G^2(x)+A(x)}{2G(x)})^2\equiv A(x)(mod\ x^{2n}) \]
等式左边的东西就是咱们要求的\(B(x)\)
因此若是说咱们知道了\(G(x)\),咱们也就能够得出\(B(x)\)啦,分母能够用多项式求逆搞一下,其余的多项式乘法fft搞一下,问题不大
总的复杂度是:
\[ T(n)=T(\frac{n}{2})+求逆复杂度+O(n\ log \ n)=O(n\ log\ n) \]
namespace NTT中主要须要调用的过程长这个样子
void Ntt_getsqrt(vct &a,vct &invb,int n,int m){ prework(a,invb,n,m); ntt(A,1); ntt(B,1); for (int i=0;i<len;++i) B[i]=1LL*B[i]*inv2%MOD*A[i]%MOD; ntt(B,-1); }
开根的话大概长这个样子
vct Sqrt(vct a){ int N=a.size(),M,M1; if (N==1){ a[0]=1; return a; } vct b=a,invb; b.resize((N+1)>>1); b=Sqrt(b); invb=b; invb.resize(N);//resize!!! invb=Inv(invb); NTT::Ntt_getsqrt(a,invb,N,N); b.resize(NTT::len); for (int i=0;i<NTT::len;++i) b[i]=(1LL*b[i]*inv2%MOD+NTT::B[i])%MOD; b.resize(N); return b; }
给出一个\(n\)次多项式\(A(x)\),以及一个\((m(m<=n)\)次多项式\(B(x)\)
要求出\(D(x)\)知足\(A(x)=D(x)B(x)+R(x)\),且\(D(x)\)的次数\(<=n-m\),\(R(x)\)的次数\(<m\)
简单来讲就是类比整数的除法,\(D(x)\)就是商,\(R(x)\)就是余数,咱们如今考虑求商
为了方便接下来的表述,先定义一些操做,咱们记:
\[ rev(A(x))=x^nA(\frac{1}{n}) \]
也就是系数反转,举个简单的例子:
\[ \begin{aligned} A(x)&=4x^4+3x^3+2x^2+1\\ rev(A(x))&=x^4+2x^3+3x^2+4 \end{aligned} \]
那么如今咱们把上面那条式子搬下来:
\[ A(x)=D(x)B(x)+R(x) \]
(接下来的步骤均将\(D(x)\)当作\(n-m\)次多项式,\(R(x)\)当作\(m-1\)次多项式,对于那些不存在的高次项咱们就把系数当作\(0\)就行了)
后面的余数看起来十分不友善,因此咱们要想个办法把它去掉,因而咱们能够进行如下的操做:
咱们将上面式子中的全部\(x\)换成\(\frac{1}{x}\),而后等式两边同时乘上\(x^n\),获得:
\[ \begin{aligned} x^nA(\frac{1}{x})&=x^{n-m}D(\frac{1}{x})x^mB(\frac{1}{x})+x^{n-m+1}x^{m-1}R(\frac{1}{x})\\ rev(A(x))&=rev(D(x))rev(B(x))+x^{n-m+1}rev(R(x))\\ \end{aligned} \]
如今再来看一下各个项的最高次项,首先是咱们要求的元素\(D(x)\),因为这个多项式原来是\(n-m\)次,因此在系数反转以后确定不会超过\(n-m\)次,而咱们要“消掉”的\(R(x)\)原来是\(m-1\)次多项式,因此\(x^{n-m+1}R(x)\)的最低次项应该是大于\(n-m\) 的
那么考虑将上面的式子放到\(mod\ x^{n-m+1}\)下,\(x^{n-m+1}R(x)\)的影响就能够十分愉快滴被消掉啦,同时咱们也不会影响到\(D(x)\)的求解,由于\(D(x)\)是\(n-m\)次的(疯狂%miskcoo太强了qwq)
因而咱们就获得了这样一个式子:
\[ rev(A(x))\equiv\ rev(D(x))rev(B(x))\ (mod\ x^{n-m+1}) \]
那因此,咱们只要求一个\(rev(B(x))\)在\(mod x^{n-m+1}\)意义下的逆元而后跟\(rev(A(x))\)乘一下,获得\(rev(D(x))\),而后再把系数反转回来就获得\(D(x)\)啦
除法大概是长这个样子
vct operator / (vct a,vct b){ int N=a.size()-1,M=b.size()-1; if (N<M){ d.resize(1);d[0]=0; return d; } reverse(a.begin(),a.end()); reverse(b.begin(),b.end()); b.resize(N-M+1); d=Inv_p(b)*a; d.resize(N-M+1); reverse(d.begin(),d.end()); return d; }
这个。。其实就是求上面那个\(R(x)\)
有了多项式除法(也就是求商)以后,求余数就变得比较简单了
类比整数的取模,咱们能够获得这样的一个式子:
\[ R(x)=A(x)-D(x)B(x) \]
那就除法求出\(D(x)\)以后直接减一下就行了,\(D(x)B(x)\)这个多项式乘法也是直接用\(fft\)求就行了
伪装很是短的样子 (然而前面的东西都是要写的qwq醒醒)
void mod(vct &a,vct b){ int N=a.size()-1,M=b.size()-1; if (N<M) return; t=a/b; a=a-(t*b); a.resize(M); }
大概。。就先写这么多吧ovo