多项式小记

先粘个 NTTFFT板子

inline void times(LL *f,LL *g,int n,int lim){
	int kn=initr(n); NTT(f,kn,1); NTT(g,kn,1);
	for(int i=0;i<kn;i++) f[i]=f[i]*g[i]%Mod;
	NTT(f,kn,-1); NTT(g,kn,-1);
	clr(f,lim,kn); 
}

分治 NTT/FFT

大概就是维护下面这种较简单半在线卷积。

给定序列 g1n1,求序列 f0n1

其中 fi=j=1ifijgj,边界为 f0=1

运用 CDQ 分治的思想,将区间 [L,R) 的求解 转化为 [L,mid)[mid,R) 的求解。

先求解 [L,mid) 相当于求出了左半部分的 f ,然后做一次 NTT 将左边的贡献加到右边,不断分治求解。

策略:

F[0,mid)G[0,R)F[mid,R)

模板题代码:

void DivNTT(int L,int R){
	if(L==R-1) return ;
	int mid=(L+R)/2,p=R-L; DivNTT(L,mid);
	cpy(f2,f+L,p/2); cpy(g2,g,p); clr(f2,p/2,2*p); clr(g2,p,2*p);
	times(f2,g2,2*p);
	for(int i=mid;i<R;i++) (f[i]+=f2[i-L])%=Mod;
	DivNTT(mid,R);
}

具体分治过程应视题目而定。

更加真实的半在线卷积:
已知 F[0]=G[0]=1,F[1]=1

G[n] 表示 F1..n 的前缀和。
Fi=j=0i1Fi1jGj

这样按照我们原先的分治方法是不行的,因为右半部分的 g 我们还不知道。
正确策略:

  • L=0

    F[0,mid)G[0,mid)F[mid,R)

  • L0

    G[L,mid)F[0,RL)F[L,mid)G[0,RL)}F[mid,R)

void solve(int L,int R){
	if(L+1==R)  { if(L) g[L]=(g[L-1]+f[L])%Mod; return ;}
	int mid=(L+R)/2,p=R-L; solve(L,mid);
	if(!L){
		cpy(f2,f,p/2); clr(f2,p/2,p);
		cpy(g2,g,p/2); clr(g2,p/2,p);
		times(f2,g2,p,0);
		for(int i=mid;i<R;i++) (f[i]+=f2[i-1])%=Mod;
	}
	else {
			else {
		cpy(f2,f+L,p/2); clr(f2,p/2,p);	cpy(g2,g,p);
		times(f2,g2,p,0);
		for(int i=mid;i<R;i++) (f[i]+=f2[i-1-L])%=Mod;
		cpy(g2,g+L,p/2); clr(g2,p/2,p); cpy(f2,f,p);
		times(f2,g2,p,0);
		for(int i=mid;i<R;i++) (f[i]+=f2[i-1-L])%=Mod;
	}
	}
	solve(mid,R);
}

例题:ABC315EX

多项式求逆

给定多项式 F(x) ,求多项式 G(x) 使得 F(x)G(x)1(mod xn)

求解:

采用倍增的过程。

我们已经求出了 F(x)P(x)1(mod xn2)

因为 F(x)G(x)1(mod xn)

所以 F(x)G(x)1(mod xn2)

F(x)(G(x)P(x))0(mod xn2)

G(x)P(x)0(mod xn2)

平方得 G(x)2+P(x)22G(x)P(x)0(mod xn)

移项,同乘 F(x) ,G(x)2P(x)F(x)P(x)2(mod xn)

不断倍增求出 G

void invp(LL *f,int n){
	static LL g[M+5],h[M+5],ff[M+5];int p=2;
	g[0]=Pow(f[0],Mod-2); 
	for(;p<(n<<1);p<<=1){
		int pn=p<<1; cpy(ff,f,p); cpy(h,g,p);
		initr(pn); NTT(ff,pn,1); NTT(h,pn,1);
		for(int i=0;i<pn;i++) g[i]=((2ll-ff[i]*h[i]%Mod)*h[i]%Mod+Mod)%Mod;
		NTT(g,pn,-1);  clr(g,p,pn);
	}
	cpy(f,g,n); clr(g,0,p); clr(h,0,p); clr(ff,0,p);
}

多项式开根

同样地倍增。
给定多项式 F(x) ,求多项式 G(x) 使得 G(x)2F(x)(mod xn)
多项式 F 的只有常数项时,用二次剩余解出 G 的常数项。蒟蒻不会。

在模板题中,常数项为 1,直接令 G 的常数项为 1.

已知 P(x)2A(x)(mod xn2)

G(x)P(x)(mod xn2)

G(x)P(x)0(mod xn2)

平方,G(x)22G(x)P(x)+P(x)20(modxn)

G(x)G(x)2+P(x)22P(x)F(x)+P(x)22P(x)P(x)+F(x)P(x)2(mod xn)

void Sqrtp(LL *f,int n){
	static LL ff[M+5],gg[M+5],g[M+5]; memset(g,0,sizeof(g));
	g[0]=1; int p=2;
	for(;p<(n<<1);p<<=1) {
		int pn=p<<1; cpy(ff,f,p); cpy(gg,g,p);
		invp(gg,p); initr(pn);
		NTT(ff,pn,1); NTT(gg,pn,1);
		for(int i=0;i<pn;i++) ff[i]=ff[i]*gg[i]%Mod;
		NTT(ff,pn,-1);
		for(int i=0;i<p;i++) g[i]=(g[i]+ff[i])%Mod*inv2%Mod;
		clr(g,p,pn);
	}
	cpy(f,g,n); clr(g,0,p); clr(ff,0,p); clr(gg,0,p);
}

多项式除法

给定 n 次多项式 F(x)m 次多项式 G(x) 。求多项式 Q(x)R(x)

满足 F(x)=G(x)Q(x)+R(x)

其中 Q(x) 的次数为 nmR(x) 的次数为 m1
定义 n 次多项式反转操作 FT(x)=xnF(x1)

例子:如 F(x)=3x2+2x+1,FT(x)=x3F(x1)=x3+2x2+3

因为 F(x)=G(x)Q(x)+R(x)

所以 F(x1)=G(x1)Q(x1)+R(x1)

同时乘 xnxnF(x1)=xnQ(x1)G(x1)+xnR(x1)

FT(x)=QT(x)GT(x)+xnm+1RT(x)

得到 QT(x)=FT(x)GT(x)1(mod xnm+1)
求出 QT(x) 并反转得到 Q(x)

R(x)=F(x)Q(x)G(x)

可能是对的代码???

inline void rev(LL *f,int n){
	for(int i=0,j=n-1;i<j;i++,j--) swap(f[i],f[j]);	
}
inline void mof(LL *f,LL *g,int n,int m){
	static LL q[M+5],r[M+5];
	int L=n-m+1;
	rev(g,m); cpy(q,g,L); rev(g,m); invp(q,L);
	rev(f,n); cpy(r,f,L); rev(f,n); times(q,r,L+L-1,L);
	rev(q,L); prta(g,m+L-1);
	times(g,q,m+L-1,m-1);
	for(int i=0;i<m-1;i++) g[i]=(f[i]-g[i]+Mod)%Mod;
	cpy(f,q,L); clr(f,L,n); clr(q,0,m); clr(r,0,L);
}

一些看不懂的东西:

简单微积分:
定义多项式 F(x)=i=0naixi

  • 定义复合多项式 F(G(x))=i=0naiG(x)i

  • 定义 F(x) 的求导: F(x)=i=0n1(i+1)ai+1xi

  • 定义 F(x) 的积分: (F(x))=i=1nai1xii

  • 复合多项式的求导:根据链式法则得,若A(x)=F(G(x)),同时对两边得主元 x 求导,则有 A(x)=F(G(x))G(x)

inline void Getdao(LL *f,int n){
	for(int i=1;i<n;i++) f[i-1]=f[i]*i%Mod;
	f[n-1]=0;
}
inline void Getjf(LL *f,int n){
	for(int i=n-1;i>=0;i--) f[i]=f[i-1]*inv[i]%Mod;
	f[0]=0;
}

看得出来,积分和求导其实是互逆的过程。

多项式牛顿迭代 。

已知函数 GG(F(x))=0,求多项式 F(mod xn)

定义 F 表示 F(mod xn2)

结论:F(x)=F(x)G(F(x))G(F(x))

如此倍增的复杂度为 O(nlogn+O(n/2))=O(nlogn)

多项式对数函数和指数函数(多项式 ln)

ex=a,则 ln(a)=x

ex=a, 则 exp(x)=a

由麦克劳林级数定义:

ln(x)=i=1(1x)ii

exp(x)=i=0xii!

一些性质:exp(ln(x))=x,exp(ln(F(x))+ln(G(x)))=F(x)G(x)

给定多项式 F(x)ln(F(x))exp(F(x))

ln

给定多项式 F 求多项式 G(x)ln(F(x))(mod xn)。保证 a0=1

ln(x)=1x

G(x)ln(F(x)) 两边同时求导。

G(x)ln(F(x))F(x)F(x)F(x)(mod xn) ,再用积分求出 G=(G(x))

inline void lnp(LL *f,int n){
	static LL g[M+5];
	cpy(g,f,n); invp(g,n);
	Getdao(f,n); times(f,g,n+n-1,n);
	Getjf(f,n); clr(g,0,n);	
} 

exp

给定多项式 F 求多项式 G(x)exp(F(x))(mod xn)。保证 a0=0

利用牛顿迭代求 G,这里用 B 表示。 回忆式子 B(x)=B(x)G(B(x))G(B(x))

根据 eF(x)=B(x)ln(B(x))F(x)=0。构造 G(B(x))=ln(B(x))F(x) ,所以 G(B(x))=ln(B(x))=B(x)1

B(x)=B(x)ln(B(x))F(x)Bx1=(1ln(B(x))+F(x))B(x)

inline void exp(LL *f,int n){
	static LL g[M+5],gg[M+5]; int p=2;
	g[0]=1;
	for(;p<(n<<1);p<<=1){
		cpy(gg,g,p); lnp(gg,p); 
		for(int i=0;i<p;i++) gg[i]=(f[i]-gg[i]+Mod)%Mod;
		gg[0]=(gg[0]+1)%Mod;	 times(g,gg,p*2,p);
	}
	cpy(f,g,p); clr(g,0,p); clr(gg,0,p);
}

多项式快速幂

给定 n1 次多项式 F(x)F(x)k(mod xn)

暴力 NTT

对于常见的题目中,一般是加速 dp 的求解。我们可以采用 O(nlognlogk) 。即写成一般快速幂的形式,用 times 函数来让多项式相乘。

这种做法还有以下优点:

见例题: 有 n 个数 a1...n,构造一个序列 b1..T(bia) ,求 Sbpmodm 。我们有一个矩阵加速做法,但是为复杂度 p3logT

但是我们思考以下,这个矩阵相乘的形式为: F[k]=i+jkF[i]×G[j]

这是一个加法卷积的形式,我们可以做一遍加法卷积之后再让 F[k]+=F[k+m]

然后用暴力的倍增求快速幂即可。但是 O(nlogn) 的做法只能维护普通的 F(x)k(mod xn) 的形式。

利用 lnexp

模板题弱化版:保证常数项为 1

Fk(x)eln(F(x))×k(mod xn)

Ei 大神的博客可以知道 k 可以直接对模数取模。

inline void polypow(LL *f,int n,int k){
	lnp(f,n); 
	for(int i=0;i<n;i++) f[i]=f[i]*k%Mod;
	exp(f,n);
}

强化版 :

不保证常数项为 1

我们可以取一个常数 c 。对用 c 除多项式 F,使得常数项为 1

那么 Fk(x)=F2k(x)×ck

但是这里,f0=0 会出问题。
所以我们找到次数最小的非零项 设为 c×xpx

Fk(x)=(F(x)c×xpx)k×ck×xpx×k

左边这个多项式的快速幂可以用上面的代码求出。

在这个结果前面接 px×k0 即可。

注意,这里的 ck 根据费马小定理,k 对模数减一取模。

inline void polypow(LL *f,int n,int k1,int k2){
	int px=0; while(f[px]==0 && px<n) px++;
	if(px==n) return ;
	if(1ll*k2*px>=n) {clr(f,0,n); return ;}
	LL iv1=Pow(f[px],Mod-2),x=Pow(f[px],k2);
	for(int i=px;i<n;i++) f[i]=f[i]*iv1%Mod;
	lnp(f+px,n-px);
	for(int i=px;i<n;i++) f[i]=f[i]*k1%Mod;
	exp(f+px,n-px);
	for(int i=px;i<n;i++) f[i]=f[i]*x%Mod;
	static LL g[M+5];
	for(int i=k2*px;i<n;i++) g[i]=f[px+i-k2*px];
	cpy(f,g,n); clr(g,0,n);
}
posted @   Shui_dream  阅读(20)  评论(0编辑  收藏  举报
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示