多项式

形如 f(x)=a0x0+a1x1+a2x2++an1xn1

点值表示法:通过代入 n 个不同的值 x0,x1xn1f(x) 中,得到 y0,y1...yn1,用 (x0,y0),(x1,y1)(xn1,yn1) 即可表示这个多项式。

若用点值表示法,并且两个多项式的 x 对应相等,那么将 y 对应相乘,即可 O(n) 的得到它们乘积的点值表示法。

从多项式的系数表示向点值表示的转化,称为求值,从多项式的点值表示向系数表示的转化,称为插值。

1|0FFT

快速傅里叶变换可以做到 O(nlogn) 进行求值和插值。

复数相乘,模长相乘,幅角相加。

(a+bi)×(c+di)=(acbd)+(bc+ad)i

在复平面上,以原点为圆心,1 为半径作圆,所得的圆叫单位圆。以圆点为起点,圆的 n 等分点为终点,做 n 个向量,设幅角为正且最小的向量对应的复数为 ωn,称为 n 次单位根。

ωnk=cos2πkn+isin2πknω2n2k=ωnkωnk+n2=ωnk

f(x)=a0x0+a1x1+a2x2+a3x3++an2xn2+an1xn1

进行奇偶分类得:

f1(x)=a0+a2x1+a4x2+a6x3++an2xn21f2(x)=a1+a3x1+a5x2+a7x3++an1xn21

f(x)=f1(x2)+xf2(x2)

k<n2,代入 ωnk 得:

f(ωnk)=f1(ωn2k)+ωnkf2(ωn2k)

再代入 ωnk+n2

f(ωnk+n2)=f1(ωn2k)ωnkf2(ωn2k)

通过这两个式子,我们就可以进行递归求解了,但因递归常数过大,我们通过迭代来实现。

我们将原多项式系数重新排序,将每个系数都在它递归的最后位置,就可以用迭代来代替递归实现。

发现原来在第 a 个位置的系数,在递归的最后位置为 a 的二进制反转以后的数。

这称为蝴蝶操作。

代入 ωnk,可再次求得一系列值,将其除以 n 即可求得多项式相乘后的系数表示。

struct Complex { double x,y; Complex(double a=0,double b=0) { x=a,y=b; } }f[maxn],g[maxn]; Complex operator +(const Complex &a,const Complex &b) { return Complex(a.x+b.x,a.y+b.y); } Complex operator -(const Complex &a,const Complex &b) { return Complex(a.x-b.x,a.y-b.y); } Complex operator *(const Complex &a,const Complex &b) { return Complex(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x); } int calc(int n) { int lim=1; while(lim<=n) lim<<=1; for(int i=0;i<lim;++i) rev[i]=(rev[i>>1]>>1)|((i&1)?lim>>1:0); return lim; } void FFT(Complex *a,int lim,int type) { for(int i=0;i<lim;++i) if(i<rev[i]) swap(a[i],a[rev[i]]); for(int len=1;len<lim;len<<=1) { Complex T(cos(Pi/len),type*sin(Pi/len)); for(int i=0;i<lim;i+=len<<1) { Complex t(1,0); for(int j=i;j<i+len;++j,t=t*T) { Complex x=a[j],y=t*a[j+len]; a[j]=x+y,a[j+len]=x-y; } } } if(type==1) return; for(int i=0;i<lim;++i) a[i].x=a[i].x/lim+0.5; } void mul(Complex *f,Complex *g) { int lim=calc(n+m); FFT(f,lim,1),FFT(g,lim,1); for(int i=0;i<lim;++i) f[i]=f[i]*g[i]; FFT(f,lim,-1); }

2|0NTT

a,p 互素,且 p>1,对于 an1(modm) 最小的 n,称为 ap 的阶,记作 δp(a)

p 是正整数,a 是整数,若 δp(a)=φ(p),则称 a 为模 p 的一个原根。

原根个数不唯一。

P 为素数,设 GP 的原根,那么 GimodP,(i<G<P,0<i<P) 的结果两两不同。

模数有 998244353,1004535809,469762049,原根都为 3

ωngp1n(modp)

int calc(int n) { int lim=1; while(lim<=n) lim<<=1; for(int i=0;i<lim;++i) rev[i]=(rev[i>>1]>>1)|((i&1)?lim>>1:0); return lim; } void NTT(ll *a,int lim,int type) { for(int i=0;i<lim;++i) if(i<rev[i]) swap(a[i],a[rev[i]]); for(int len=1;len<lim;len<<=1) { ll wn=qp(type==1?G:Gi,(P-1)/(len<<1)); for(int i=0;i<lim;i+=len<<1) { ll w=1; for(int j=i;j<i+len;++j,w=w*wn%P) { ll x=a[j],y=w*a[j+len]%P; a[j]=(x+y)%P,a[j+len]=(x-y+P)%P; } } } if(type==1) return; ll inv=qp(lim,P-2); for(int i=0;i<lim;++i) a[i]=a[i]*inv%P; } void mul(ll *f,ll *g) { int lim=calc(n+m); NTT(f,lim,1),NTT(g,lim,1); for(int i=0;i<lim;++i) f[i]=f[i]*g[i]%P; NTT(f,lim,-1); }

3|0多项式求导和积分

[xi]f(x)=(i+1)[xi+1]f(x)[xi]f(x)=1i[xi1]f(x)

4|0多项式牛顿迭代

已知 f(x),求 g(x),满足 f(g(x))0(modxn)

设已经求得 g0(x),满足 g0(x)g(x)(modxn),由泰勒展开得:

0=f(g(x))=f(g0(x))+f(g0(x))(g(x)g0(x))+f(g0(x))2(g(x)g0(x))2+f(g0(x))+f(g0(x))(g(x)g0(x))(modx2n)

得:

g(x)g0(x)f(g0(x))f(g0(x))(modx2n)

5|0多项式求逆

f(x)g(x)1(modxn)

g(x)f(x) 的逆元,模 xn 的意义是将次数大于等于 n 的项都忽略掉。

设已经求得满足

f(x)g(x)1(modxn)

g(x),得:

f(x)g(x)10(modxn)(f(x)g(x)1)20(modx2n)f(x)(2g(x)g2(x)f(x))1(modx2n)

本质为牛顿迭代

g(x)=f1(x)h(g(x))=g1(x)f(x)=0

设已经求得 g0(x),满足 g0(x)g(x)(modxn),得:

g(x)g0(x)h(g0(x))h(g0(x))(modx2n)g0(x)g01(x)f(x)g02(x)(modx2n)2g0(x)g02(x)f(x)(modx2n)

6|0多项式对数函数

g(x)=lnf(x)g(x)=f(x)f(x)

7|0多项式指数函数

g(x)=ef(x)h(g(x))=lng(x)f(x)=0

设已经求得 g0(x),满足 g0(x)g(x)(modxn),得:

g(x)g0(x)h(g0(x))h(g0(x))(modx2n)g0(x)lng0(x)f(x)g01(x)(modx2n)g0(x)(1lng0(x)+f(x))(modx2n)

8|0多项式幂函数

fk(x)=eklnf(x)

9|0多项式开根

可以直接使用多项式幂函数求解,也可以用牛顿迭代求解。

g2(x)=f(x)h(g(x))=g2(x)f(x)=0

设已经求得 g0(x),满足 g0(x)g(x)(modxn),得:

g(x)g0(x)h(g0(x))h(g0(x))(modx2n)g0(x)g02(x)f(x)2g0(x)(modx2n)g02(x)+f(x)2g0(x)(modx2n)

10|0多项式除法

已知 n 次多项式 f(x)m 次多项式 g(x),求满足 f(x)g(x)h(x)+t(x)(modxn+1)nm 次多项式 h(x) 和次数小于 m 的多项式 t(x)

fr(x) 为多项式 f(x) 系数翻转得到的多项式,得 fr(x)=xnf(x1)

f(x)g(x)h(x)+t(x)(modxn+1)xnf(x1)xmg(x1)xnmh(x1)+xnt(x1)(modxn+1)fr(x)gr(x)hr(x)+xnm+1tr(x)(modxn+1)fr(x)gr(x)hr(x)(modxnm+1)hr(x)fr1(x)gr(x)(modxnm+1)

求出 h(x) 后,得 t(x)f(x)g(x)h(x)(modxm)

11|0递推

多项式操作的 O(n2) 递推。

11|1多项式求逆

F(x)G(x)=1i=0nfigni=[n=0]gn=1f0i=1nfigni

边界为 g0=inv(f0)

11|2多项式对数函数

G(x)=lnF(x)G(x)=F(x)F(x)i=0n(i+1)gi+1fni=(n+1)fn+1(n+1)gn+1f0=(n+1)fn+1i=0n1(i+1)gi+1fnign+1=1(n+1)f0((n+1)fn+1i=0n1(i+1)gi+1fni)gn=1nf0(nfni=1n1igifni)

11|3多项式指数函数

G(x)=eF(x)G(x)=G(x)F(x)(n+1)gn+1=i=0n(i+1)fi+1gnign+1=1(n+1)i=0n(i+1)fi+1gnign=1ni=1nifigni

边界为 g0=1

12|0模板

void Inv(int deg,ll *a,ll *b) { static ll t[maxn]; if(deg==1) { b[0]=qp(a[0],P-2); return; } Inv((deg+1)>>1,a,b); int lim=calc(deg<<1); for(int i=0;i<deg;++i) t[i]=a[i]; for(int i=deg;i<lim;++i) t[i]=b[i]=0; NTT(t,lim,1),NTT(b,lim,1); for(int i=0;i<lim;++i) b[i]=b[i]*(2-t[i]*b[i]%P+P)%P; NTT(b,lim,-1); for(int i=deg;i<lim;++i) b[i]=0; } void Ln(int deg,ll *a,ll *b) { static ll inva[maxn],dera[maxn]; Inv(deg,a,inva); for(int i=0;i<deg-1;++i) dera[i]=a[i+1]*(i+1)%P; dera[deg-1]=0; int lim=calc(deg<<1); for(int i=deg;i<lim;++i) dera[i]=inva[i]=0; NTT(dera,lim,1),NTT(inva,lim,1); for(int i=0;i<lim;++i) b[i]=dera[i]*inva[i]%P; NTT(b,lim,-1); for(int i=deg-1;i>=1;--i) b[i]=b[i-1]*qp(i,P-2)%P; b[0]=0; for(int i=deg;i<lim;++i) b[i]=0; } void Exp(int deg,ll *a,ll *b) { static ll t[maxn],lnb[maxn]; if(deg==1) { b[0]=1; return; } Exp((deg+1)>>1,a,b),Ln(deg,b,lnb); int lim=calc(deg<<1); for(int i=0;i<deg;++i) t[i]=(a[i]-lnb[i]+P)%P; for(int i=deg;i<lim;++i) t[i]=b[i]=0; NTT(t,lim,1),NTT(b,lim,1); for(int i=0;i<lim;++i) b[i]=b[i]*(1+t[i])%P; NTT(b,lim,-1); for(int i=deg;i<lim;++i) b[i]=0; } void Pow(int deg,ll *a,ll *b,ll k) { static ll lna[maxn]; Ln(deg,a,lna); for(int i=0;i<deg;++i) lna[i]=lna[i]*k%P; Exp(deg,lna,b); } void Sqrt(int deg,ll *a,ll *b) { static ll t[maxn],invb[maxn]; if(deg==1) { b[0]=1; return; } Sqrt((deg+1)>>1,a,b); int lim=calc(deg<<1); for(int i=0;i<deg;++i) t[i]=2*b[i]%P,invb[i]=0; for(int i=deg;i<lim;++i) t[i]=invb[i]=0; Inv(deg,t,invb); for(int i=0;i<deg;++i) t[i]=a[i]; for(int i=deg;i<lim;++i) t[i]=b[i]=0; NTT(t,lim,1),NTT(b,lim,1),NTT(invb,lim,1); for(int i=0;i<lim;++i) b[i]=(b[i]*b[i]%P+t[i])%P*invb[i]%P; NTT(b,lim,-1); for(int i=deg;i<lim;++i) b[i]=0; }

__EOF__

本文作者lhm_
本文链接https://www.cnblogs.com/lhm-/p/12229547.html
关于博主:sjzez 的一名 OI 学生
版权声明:转载标明出处
声援博主:希望得到宝贵的建议
posted @   lhm_liu  阅读(584)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示