多项式模板整理

约定#

F[i] 表示 F(x)i 次项系数。

多项式乘法基础#

详情见 多项式乘法入门

多项式求逆#

给出 n 次多项式 F(x),求一个多项式 G(x),满足 F(x)G(x)1(modxn)G(x) 每个系数对 998244353 取模。

我们逐一递推,假设已知 G[1...i1],需要求 G[i] 的值。若 i=1,显然 G[1]=1F[1];否则,有

j=0iF[j]G[ij]=0

把带 G[i] 的提出来

F[0]G[i]+j=0i1F[j]G[ij]=0

G[i]=j=0i1F[j]G[ij]F[0]

该算法的时间复杂度为 O(n2)

我们尝试使用 倍增法 求解。

若我们已知 n2 次多项式 G0(x) 满足

F(x)G0(x)1(modxn2)

那么由于

F(x)G(x)1(modxn)

模数变为 n2

F(x)G(x)1(modxn2)

两式相减得

F(x)(G(x)G0(x))0(modxn2)

G(x)G0(x)0(modxn2)

为了把模数变为 n,两边平方

(G(x)G0(x))20(modxn)

同时乘 F(x)

F(x)(G(x)G0(x))20(modxn)

展开

F(x)G2(x)2F(x)G(x)G0(x)+F(x)G02(x)0(modxn)

带入 F(x)G(x)1(modxn)

G(x)2G0(x)+F(x)G02(x)0(modxn)

移项得

G(x)2G0(x)F(x)G02(x)(modxn)

其实也可以牛顿迭代。

点击查看代码
ll Q[maxn],P[maxn],o[maxn],t;
	void Getinv(ll *a,ll *b,ll n){
		for(ll i=0;i<n;i++) Q[i]=0; t=0;
		while(n>1) o[++t]=n, n=n+1>>1;
		Q[0]=power(a[0]); o[++t]=1;
		for(ll i=t-1;i;i--){
			for(ll j=0;j<o[i+1];j++) P[j]=Q[j];
			mul(P,P,P,o[i+1],o[i+1],o[i]);
			mul(a,P,P,o[i],o[i],o[i]);
			for(ll j=0;j<o[i];j++) Q[j]=(2*Q[j]-P[j]+mod)%mod;
		}
		for(ll i=0;i<o[1];i++) b[i]=Q[i];
	}

时间复杂度 O(nlogn)

多项式求导与积分#

求导:F(x)=i>0f[i]ixi1

积分:i>0f[i1]ixi

积分不会书写。

注意 F(x)(F(x)),可以设 y=F(x),容易得到 (F(x))=y=0

多项式 ln#

计算 lnF(x),设 G(x)=lnF(x)

两边同时求导

G(x)=(lnF)(x)

链式法则得

G(x)=lnF(x)F(x)

G(x)=F(x)F(x)

两边积分一下就行了,O(nlogn)

点击查看代码
ll R[maxn];
	void ln(ll *a,ll *b,ll n){
		dao(a,R,n);
		Getinv(a,b,n);
		mul(b,R,R,n,n,n);
		jifen(R,b,n);
	}

多项式 exp#

F(G(x))=lnG(x)A(x),我们要求的是 G(x),满足方程 F(G(x))=0

根据牛顿迭代,我们求出了 G0(x)0(modx)n2,由于 A(x) 是已知的东西,则

G(x)G0(x)F(G0(x))F(G0(x))(modxn)

G(x)G0(x)lnG0(x)A(x)lnG0(x)(modxn)

G(x)G0(x)lnG0(x)A(x)1G0(x)(modxn)

G(x)G0(x)G0(x)(lnG0(x)A(x))(modxn)

G(x)G0(x)(1lnG0(x)+A(x))(modxn)

分治即可,O(nlogn)

点击查看代码
ll z[maxn],zlt,F[maxn],G[maxn];
	void Exp(ll *a,ll *b,ll n){
		zlt=0;
		for(ll i=0;i<n;i++) F[i]=0;
		while(n>1) z[++zlt]=n, n=n+1>>1;
		z[++zlt]=1; F[0]=1;
		for(ll i=zlt-1;i;i--){
			ln(F,G,z[i]);
			for(ll j=0;j<z[i];j++) G[j]=(a[j]<G[j]? a[j]+mod-G[j]:a[j]-G[j]);
			G[0]=(G[0]==mod-1? 0:G[0]+1);
			mul(F,G,F,z[i+1],z[i],z[i]);
		}
		for(ll i=0;i<z[1];i++) b[i]=F[i];
	}

多项式开根#

F(G(x))=G2(x)A(x),求 F(G(x))=0 的解。

exp 基本一模一样

G(x)G0F(G0(x))F(G0(x))(modxn)

G(x)G0G02(x)A(x)2G0(x)(modxn)

G(x)A(x)+G02(x)2G0(x)(modxn)

点击查看代码
void Sqrt(ll *a,ll *b,ll n){
		zlt=0;
		for(ll i=0;i<n;i++) F[i]=0;
		while(n>1) z[++zlt]=n, n=n+1>>1;
		z[++zlt]=1; F[0]=1;
		for(ll i=zlt-1;i;i--){
			mul(F,F,G,z[i+1],z[i+1],z[i]);
			for(ll j=0;j<z[i];j++) G[j]=(G[j]+a[j]>=mod? G[j]+a[j]-mod:G[j]+a[j]);
			for(ll j=0;j<z[i+1];j++) F[j]=((F[j]<<1)>=mod? (F[j]<<1)-mod:F[j]<<1);
			Getinv(F,F,z[i]);
			mul(G,F,F,z[i],z[i],z[i]);
		}
		for(ll i=0;i<z[1];i++) b[i]=F[i];
	}

多项式除法#

已知 F(x),G(x),解方程 F(x)=P(x)G(x)+Q(x),其中 degQ(x)<degG(x)

degF(x)=n, degG(x)=m

推导:

F(1x)=P(1x)G(1x)+Q(1x)

xnF(1x)=xnP(1x)G(1x)+xnQ(1x)

xnF(1x)=xnmP(1x)xmG(1x)+xnQ(1x)

FR(x)=PR(x)GR(x)+xnm+1QR(x)

上式中形如 DR(x) 的多项式表示 D(x) 所有系数 reverse 后的多项式。

注意到右边有一项 xnm+1QR(x),当上式在模 xnm+1
意义下:

FR(x)PR(x)GR(x)(modxnm+1)

PR(x)FR(x)GR(x)(modxnm+1)

注意到 PR(x) 的次数为 nm,于是一个求逆一个乘法可得 PR(x),进而得到 P(x)

带入 F(x)=P(x)G(x)+Q(x) 即可得到 Q(x)

多项式快速幂#

给出多项式 F(x) 和正整数 n,k,求 Fk(x)(modn)

考虑到 xk=exp(klnx),于是 Fk(x)=exp(klnF(x)),直接一个 ln 一个 exp 即可。

但是 [x0]F(x) 不一定为 1,不能直接 ln,把多项式转换成 F(x)=xabG(x) 的形式,于是 Fk(x)=xakbkGk(x),注意 kbk 中对 φ(mod) 取模。

常系数齐次线性递推#

已知对于 nk,有 an=i=1kfiani,给出 f1...k,a0...k1,求 an

n109

F(fixi)=fiai,答案即为 F(xn)

构造多项式 G(x)=xki=0k1fkixi

显然,F(G(x))=F(xki=0k1fkixi)=aki=0k1fkiai=0

那么若 xn=P(x)G(x)+Q(x),那么 F(xn)=F(Q(x))

这相当于多项式取模。但是 n 太大,我们可以用快速幂,把普通乘换成 ntt,把普通取模换成多项式取模。

部分代码合集

点击查看代码
ll power(ll a,ll b=mod-2){
	ll s=1;
	while(b){
		if(b&1) s=s*a%mod;
		a=a*a%mod; b>>=1;
	} return s;
}
struct POLY{
	ll rev[maxn<<2], tr, inv[maxn<<2];
	void Getrev(ll n){
		if(tr==n) return; tr=n;
		for(ll i=1;i<n;i++)
			rev[i]=(rev[i>>1]>>1)|(i&1? n>>1:0);
	}
	void ntt(ll *a,ll n){
		Getrev(n);
		for(ll i=1;i<n;i++)
			if(i<rev[i]) swap(a[i],a[rev[i]]);
		for(ll i=1;i<n;i<<=1){
			ll g=power(3,(mod-1)/(i<<1));
			for(ll j=0;j<n;j+=(i<<1))
				for(ll k=0,g0=1;k<i;k++,g0=g0*g%mod){
					ll x=a[j|k], y=a[i|j|k]*g0%mod;
					a[j|k]=(x+y>=mod? x+y-mod:x+y), a[i|j|k]=(x<y? x+mod-y:x-y);
				}
		}
	}
	ll A[maxn<<2], B[maxn<<2];
	void mul(ll *a,ll *b,ll *c,ll n,ll m,ll k){
		for(ll i=0;i<n;i++) A[i]=a[i];
		for(ll i=0;i<m;i++) B[i]=b[i];
		ll l=1;
		while(l<n+m-1) l<<=1;
		ntt(A,l), ntt(B,l);
		for(ll i=0;i<l;i++) A[i]=A[i]*B[i]%mod;
		ntt(A,l), reverse(A+1,A+l);
		ll Inv=power(l);
		for(ll i=0;i<l;i++){
			if(i<k) c[i]=A[i]*Inv%mod;
			A[i]=B[i]=0;
		}
		for(ll i=l;i<k;i++) c[i]=0;
	}
	ll Q[maxn],P[maxn],o[maxn],t;
	void Getinv(ll *a,ll *b,ll n){
		for(ll i=0;i<n;i++) Q[i]=0; t=0;
		while(n>1) o[++t]=n, n=n+1>>1;
		Q[0]=power(a[0]); o[++t]=1;
		for(ll i=t-1;i;i--){
			for(ll j=0;j<o[i+1];j++) P[j]=Q[j];
			mul(P,P,P,o[i+1],o[i+1],o[i]);
			mul(a,P,P,o[i],o[i],o[i]);
			for(ll j=0;j<o[i];j++) Q[j]=(2*Q[j]-P[j]+mod)%mod;
		}
		for(ll i=0;i<o[1];i++) b[i]=Q[i];
	}
	void dao(ll *a,ll *b,ll n){
		for(ll i=1;i<n;i++) b[i-1]=a[i]*i%mod;
	}
	ll r=0;
	void jifen(ll *a,ll *b,ll n){
		while(r<n){
			++r;
			if(r>1) inv[r]=(mod-mod/r)*inv[mod%r]%mod;
			else inv[r]=1;
		}
		for(ll i=n-1;i>0;i--) b[i]=a[i-1]*inv[i]%mod;
		b[0]=0;
	}
	ll R[maxn];
	void ln(ll *a,ll *b,ll n){
		dao(a,R,n);
		Getinv(a,b,n);
		mul(b,R,R,n,n,n);
		jifen(R,b,n);
	}
	ll z[maxn],zlt,F[maxn],G[maxn];
	void Exp(ll *a,ll *b,ll n){
		zlt=0;
		for(ll i=0;i<n;i++) F[i]=0;
		while(n>1) z[++zlt]=n, n=n+1>>1;
		z[++zlt]=1; F[0]=1;
		for(ll i=zlt-1;i;i--){
			ln(F,G,z[i]);
			for(ll j=0;j<z[i];j++) G[j]=(a[j]<G[j]? a[j]+mod-G[j]:a[j]-G[j]);
			G[0]=(G[0]==mod-1? 0:G[0]+1);
			mul(F,G,F,z[i+1],z[i],z[i]);
		}
		for(ll i=0;i<z[1];i++) b[i]=F[i];
	}
	void Sqrt(ll *a,ll *b,ll n){
		zlt=0;
		for(ll i=0;i<n;i++) F[i]=0;
		while(n>1) z[++zlt]=n, n=n+1>>1;
		z[++zlt]=1; F[0]=1;
		for(ll i=zlt-1;i;i--){
			mul(F,F,G,z[i+1],z[i+1],z[i]);
			for(ll j=0;j<z[i];j++) G[j]=(G[j]+a[j]>=mod? G[j]+a[j]-mod:G[j]+a[j]);
			for(ll j=0;j<z[i+1];j++) F[j]=((F[j]<<1)>=mod? (F[j]<<1)-mod:F[j]<<1);
			Getinv(F,F,z[i]);
			mul(G,F,F,z[i],z[i],z[i]);
		}
		for(ll i=0;i<z[1];i++) b[i]=F[i];
	}
}D;

出处:https://www.cnblogs.com/Sktn0089/p/18022061

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   Lgx_Q  阅读(16)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示