定义
A(x):表示一个关于x的多项式
A−1(x):表示多项式A(x)的逆元
A(i)(x):表示A(x)的i阶导
多项式乘法
FFT或NTT或任意模FFT 复杂度O(nlogn)
扔一个FFT板子:
#include <cmath>
const double pi=acos(-1.0);
struct cp
{
double x,i;
cp(double _x=0,double _i=0):x(_x),i(_i){}
cp operator +(const cp &z)const {return cp(x+z.x,i+z.i);}
cp operator -(const cp &z)const {return cp(x-z.x,i-z.i);}
cp operator *(const cp &z)const {return cp(x*z.x-i*z.i,x*z.i+i*z.x);}
};
void FFT(cp a[],int len,int fl)
{
int i,j,k; cp t,w,wn;
for(i=k=0;i<len;++i)
{
if(i>k) swap(a[i],a[k]);
for(j=(len>>1);(k^=j)<j;j>>=1);
}
for(i=2;i<=len;i<<=1)
{
wn=cp(cos(2*pi*fl/i),sin(2*pi*fl/i));
for(j=0;j<len;j+=i)
{
w=cp(1,0);
for(k=j;k<j+(i>>1);++k,w=w*wn)
t=w*a[k+(i>>1)],a[k+(i>>1)]=a[k]-t,a[k]=a[k]+t;
}
}
if(fl==-1) for(i=0;i<len;++i) a[i].x/=len;
}
NTT同理,将单位复数根变成 998244353 的原根 3 的 lenphi∗k 次幂即可。
任意模FFT:对于每个数ai,拆成两部分ai=bi∗32768+ci。
那么a1i∗a2j等价于b1i∗b2j∗327682+b1i∗c2j∗32768+c1i∗b2j∗32768+c1i∗c2j分成4部分求卷积之后再加到一起,可以缩小FFT后结果大小的范围,进而实现任意模。
如果更大的话可以尝试分3或2*2段
多项式逆元
即A(x)A−1(x)=1(modxn)则称A−1(x)为A(x)在modxn意义下的逆元。
求法:
当n==1时,A−1(x)即为A(x)常数项的逆元。
倍增,已知B(x)使得A(x)B(x)=1(modx2n),求A−1(x)使得A(x)A−1(x)=1(modxn)
两式相减得:A(x)(B(x)−A−1(x))=0(modx2n)
因为A(x)不为0,所以:B(x)−A−1(x)=0(modx2n)
两边平方得:B(x)2+A−1(x)2−2∗B(x)∗A−1(x)=0(modxn),使得模数变成n次。
两边同乘A(x),移项得:A−1(x)=2B(x)−B(x)2A(x)(modxn)就做完了。
每次B(x)的项数是2n的,A是n的,所以开到2n长度就行了。
复杂度:O(nlogn) 注意每次A(x)的长度是当前mod运算的长度
void polyinv(ll A[],ll B[],int len)
{
static ll tmp[N<<2];
int M=1,i; B[0]=Pow(A[0],mod-2,mod);
while(M<len)
{
M<<=1;
memset(tmp,0,sizeof(ll)*(M<<1));
for(i=0;i<M;++i) tmp[i]=A[i];
NTT(tmp,M<<1,1); NTT(B,M<<1,1);
for(i=0;i<(M<<1);++i) B[i]=add((2*B[i]-tmp[i]*B[i]%mod*B[i])%mod+mod);
NTT(B,M<<1,-1);
for(i=M;i<(M<<1);++i) B[i]=0;
}
for(i=len;i<M;++i) B[i]=0;
}
多项式除法/取模
即A(x)=B(x)C(x)+D(x),已知n项A(x)和m项B(x),求n-m项C(x)和m-1项的D(x)。
定义运算AR(x)=xn−1A(x1),即把A(x)0 ~ n-1项的系数翻转后得到的多项式。
得:AR(x)=xn−1A(x1)=xn−1(B(x1)C(x1)+D(x1))
=xm−1B(x1)xn−mC(x1)+xn−m+1xm−2D(x1)
=BR(x)CR(x)+xn−m+1DR(x)
模xn−m+1得:AR(x)=BR(x)CR(x)(modxn−m+1
移项得:CR(x)=AR(x)BR(x)−1(modxn−m+1)
求出C(x)后回代即可求出D(x)
用AR(x)BR(x)−1求出DR(x),翻转即商,回代求模。
泰勒展开
我想用一个多项式来近似表示一个不一定是多项式的函数怎么办?
首先通过f(0)求出0次项系数a0
将f求导,发现a1变成了常数项,再通过f(1)(0)求出1次项系数。
以此推类。
注意每次求导都会使多项式系数前面乘上自己的幂次,之后幂次-1。那么最后的式子就是:
f(x)=0!f(0)+1!f(1)(0)x1+2!f(2)(0)x2...+n!f(n)(0)xn+...
注意每次都是拿0这个横坐标来逼近函数,考虑扩展到任意横坐标x0
对于定义域[L,R]的函数f(x),在L≤x0≤R具有n阶导数,则成立下式:
f(x)=0∑∞i!f(i)(x0)(x−x0)i
牛顿迭代
就是一个用已知的解逼近正确的解的迭代方法。
普通牛顿迭代:求f(x)的零点。
随便弄个x0,之后将f(x)在x0处泰勒展开:
f(x)=f(x0)+f(1)(x0)(x−x0)+...
只保留线性部分:f(x)=f(x0)+f(1)(x0)(x−x0)=0
化简得:x=x0−f(1)(x0)f(x0)
用x代替x0代入,迭代多次即可。
多项式牛顿迭代
已知g(x),g(f(x))=0(modxn),求f(x)。即求一个关于多项式f(x)的方程的解。
和求逆类似,倍增:
n==1时直接用牛顿迭代零点。
考虑已知g(B(x))=0(modx2n)求g(A(x))=0(modxn)
在B(x)处泰勒展开:g(A(x))=g(B(x))+g(1)(B(x))(A(x)−B(x))+2g(2)(B(x))(A(x)−B(x))2+...=0(modxn)
发现A(x)-B(x)的前2n项系数都是0,整个多项式还是modxn的,所以只保留前两项即可:
g(B(x))+g(1)(B(x))(A(x)−B(x))=0(modxn)
得:A(x)=B(x)−g(1)(B(x))g(B(x)),迭代即可,最后得到的是精确解。
多项式求导/积分
求导bi=(i+1)ai+1,
积分ci=iai−1
复杂度O(n)
你这求导再积分常数项不就没了么。。。
求ln(A(x))
ln(A(x))=∫ln(1)(A(x))=∫A(x)A(1)(x)=∫A(1)(x)A−1(x)
求导+求逆+求积分
多项式exp
已知A(x),求f(x)=eA(x)(modxn)
取ln得:ln(f(x))−A(x)=0(modxn)
代入牛顿迭代:f(x)=f0(x)−f0(x)1ln(f0(x))−A(x)
根据ckh口胡,因为你把f(x)看成变量了,所以A(x)就相当于常数项,求导即为0。
真的没问题么。。。
化简得:f(x)=f0(x)(A(x)−ln(f0(x))+1)
多项式的幂
求Ak(x)(modxn)
其实要是常数次方的话,可以快速幂,每次乘之后将n次方及以上的部分舍去即可,复杂度O(nlognlogk)
以下主要是处理多项式次方:
换底:Ak(x)=ek∗ln(A(x))
求ln+exp
求多项式次方的话直接把k换成B(x)即可。
多项式开根
已知A(x),求f2(x)=A(x)(modxn)
即:f2(x)−A(x)=0
牛顿迭代:f(x)=f0(x)−2f0(x)f0(x)2−A(x)
=21(f0(x)+f0(x)−1A(x))
void polysqrt(ll A[],ll B[],int len)
{
static ll tmp1[N<<2],tmp2[N<<2];
int M=1,i; B[0]=1;
while(M<len)
{
M<<=1;
memset(tmp1,0,sizeof(ll)*(M<<1));
memset(tmp2,0,sizeof(ll)*(M<<1));
polyinv(B,tmp1,M);
for(i=0;i<M;++i) tmp2[i]=A[i];
NTT(tmp1,M<<1,1); NTT(tmp2,M<<1,1);
for(i=0;i<(M<<1);++i) tmp1[i]=tmp1[i]*tmp2[i]%mod;
NTT(tmp1,M<<1,-1);
for(i=0;i<M;++i) B[i]=(B[i]+tmp1[i])*inv2%mod;
}
for(i=len;i<M;++i) B[i]=0;
}
关于迭代的第一项:
如果为double意义下,可以直接开根获得。
如果为mod意义下,对于特定的常数项直接赋值(eg. sqrt(1)=1(mod x)) ,其余的好像得二次剩余。