多项式相关模板#2

.
最近出了几个这样的题于是就把模板库完善了一下
依然是全程NTT实现,模数为998244353,依然没有多点插值和多点求值
不过这次画风大变,因为结构体化的多项式模板其实无论用什么实现方式(数组,指针,vector)都不能同时兼顾效率和灵活性两方面,所以整个代码都是用数组实现
稍微写一下一些需要注意的点:

多项式求逆

大概是基于一个思想:倍增
首先当 d e g ( A ) = 1 deg(A)=1 deg(A)=1时我们有 B = A − 1 = A M − 2 B=A^{-1}=A^{M-2} B=A1=AM2 注意常数项不能为0
下面考虑 d e g ( A ) > 1 deg(A)>1 deg(A)>1
假设我们知道了 A ( x ) ∗ B ′ ( x ) = 1   ( m o d   x n ) A(x)*B'(x)=1\ (mod\ x^n) A(x)B(x)=1 (mod xn)
现在我们要求的是 A ( x ) ∗ B ( x ) = 1   ( m o d   x 2 n ) A(x)*B(x)=1\ (mod\ x^{2n}) A(x)B(x)=1 (mod x2n)
首先必然有的就是 A ( x ) ∗ ( B ′ ( x ) − B ( x ) ) = 0   ( m o d   x n ) A(x)*(B'(x)-B(x))=0\ (mod\ x^n) A(x)(B(x)B(x))=0 (mod xn)
那么就有 B ′ ( x ) − B ( x ) = 0   ( m o d   x n ) B'(x)-B(x)=0\ (mod\ x^n) B(x)B(x)=0 (mod xn)
两边平方就是 B 2 − 2 B B ′ + B ′ 2 = 0   ( m o d   x 2 n ) B^2-2BB'+B'^2=0\ (mod\ x^{2n}) B22BB+B2=0 (mod x2n)上面这一步非常关键
接下来用前面的结论 A B ′ = 1 AB'=1 AB=1就得到
B − 2 B ′ + A B ′ 2 = 0   ( m o d   x 2 n ) B-2B'+AB'^2=0\ (mod\ x^{2n}) B2B+AB2=0 (mod x2n)
那么我们就求得了 B ( x ) = 2 B ′ − A B ′ 2 B(x)=2B'-AB'^2 B(x)=2BAB2
写的时候直接把次数取到2的幂次会比较方便


多项式求Ln

一个非常好的问题
首先定义?就是 Ln(A)的泰勒展开而已
这个时候就会发现一些问题
如果A的常数项不为1那么我们发现   l n ( A ) \ ln(A)  ln(A)是不能在模意义下表示的
所以我们这里只讨论常数项为1的情况(不为1请用FFT而不是NTT)
注意到以下的式子 d ( l n ( A ) ) = d A A d(ln(A))=\frac{dA}{A} d(ln(A))=AdA
这给我们求ln提供了方法,先对A求导,乘上A的逆元让后积分就可以了
积分常数C一般取0
这个还是比较好理解的


多项式Exp

这个就不那么好解释了
先给一个牛顿迭代的式子
如果有式子 F F F满足 G ( F ( x ) ) = 0   ( m o d   x n ) G(F(x))=0\ (mod\ x^n) G(F(x))=0 (mod xn)
那么我们就得到 H ( x ) = F ( x ) − G ( F ( x ) ) / G ′ ( F ( x ) ) H(x)=F(x)-G(F(x))/G'(F(x)) H(x)=F(x)G(F(x))/G(F(x))这个H满足 G ( H ( x ) ) = 0   ( m o d   x 2 n ) G(H(x))=0\ (mod\ x^{2n}) G(H(x))=0 (mod x2n)
我们现在要求的是 l n ( F ( x ) ) − A ( x ) = 0   ( m o d   x n ) ln(F(x))-A(x)=0\ (mod\ x^n) ln(F(x))A(x)=0 (mod xn)
G ( F ( x ) ) = l n ( F ( x ) ) − A ( x ) G(F(x))=ln(F(x))-A(x) G(F(x))=ln(F(x))A(x)带入上面那个式子就得到
H ( x ) = F ( x ) ⋅ ( 1 − l n ( F ( x ) ) + A ( x ) H(x)=F(x)·(1-ln(F(x))+A(x) H(x)=F(x)(1ln(F(x))+A(x)
这样,套用上面求逆的倍增法就可以了
同样注意,只有当常数项为0的时候这个算法才能使用否则请用FFT


多项式开方

依然是沿用上面的倍增方法
我们考虑已经得到 B ′ 2 = A   ( m o d   x n ) B'^2=A\ (mod\ x^n) B2=A (mod xn)
那么就得到 ( B + B ′ ) ∗ ( B − B ′ ) = 0   ( m o d   x n ) (B+B')*(B-B')=0\ (mod\ x^n) (B+B)(BB)=0 (mod xn)
取一个解就得到 B − B ′ = 0   ( m o d   x n ) B-B'=0\ (mod\ x^n) BB=0 (mod xn)
平方一下就得到 B 2 − 2 B B ′ + B ′ 2 = 0   ( m o d   x 2 n ) B^2-2BB'+B'^2=0 \ (mod\ x^{2n}) B22BB+B2=0 (mod x2n)
又因为我们已知 B 2 = A B^2=A B2=A
所以就有 B ( x ) = A ( x ) + B ′ 2 ( x ) 2 B ′ ( x ) B(x)=\frac{A(x)+B'^2(x)}{2B'(x)} B(x)=2B(x)A(x)+B2(x)
套用上面的多项式求逆就可以了

除法这里暂时不写(代码里面有)

#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 400010
#define M 998244353
#define LL long long
using namespace std;
int W[N],iW[N],inv[N];
int rA[N],rB[N],C[N],b[N],iA[N],iB[N],dA[N];
inline LL pow(LL x,LL k,LL s=1){
	for(;k;x=x*x%M,k>>=1) k&1?s=s*x%M:0;
	return s;
}
inline void init(){
	inv[1]=1;
	for(int j=2;j<N;++j)
		inv[j]=(LL)(M-M/j)*inv[M%j]%M;
	for(int j=1;j<=N;j<<=1){
		W[j]=pow(3,(M-1)/j);
		iW[j]=pow(W[j],M-2);
	}
}
inline void NTT(int* A,int n,int g){
	static int b[N]; memcpy(b,A,n<<2);
	for(int i=0,j=0;i<n;i++){
        if(i>j) swap(b[i],b[j]);
        for(int l=n>>1;(j^=l)<l;l>>=1);
    }
	for(int k=1;k<n;k<<=1){
		register LL w=g?W[k<<1]:iW[k<<1],u,v,z=1;
		for(int j=k;j<n;++j&k?z=z*w%M:z=1,j|=k){
			u=b[j^k]; v=z*b[j]%M;
			b[j^k]=(u+v)%M; b[j]=(u-v+M)%M;
		}
	}
	if(!g){
		g=inv[n];
		for(int i=0;i<n;++i) A[i]=(LL)b[i]*g%M;
	} else memcpy(A,b,n<<2);
}
inline void gInv(int* A,int* B,int n){
	if(n==1){ *B=pow(*A,M-2); return; }
	gInv(A,B,n>>1);
	for(int i=0;i<n;++i) C[i]=A[i],C[i+n]=0;
	NTT(C,n<<1,1);
	NTT(B,n<<1,1);
	for(int i=0;i<(n<<1);++i) C[i]=B[i]*(M+2-(LL)C[i]*B[i]%M)%M;
	NTT(C,n<<1,0);
	for(int i=0;i<n;++i) B[i]=C[i],B[i+n]=0;
}
inline void Sqrt(int* A,int* B,int n){
	if(n==1){ *B=*A; return; }
	Sqrt(A,B,n>>1);
	for(int i=0;i<n;++i) C[i]=A[i],iB[i]=iB[i+n]=C[i+n]=0;
	gInv(B,iB,n);
	NTT(C,n<<1,1);
	NTT(iB,n<<1,1);
	for(int i=0;i<(n<<1);++i) C[i]=(LL)C[i]*inv[2]%M*iB[i]%M;
	NTT(C,n<<1,0);
	for(int i=0;i<n;++i) B[i]=((LL)B[i]*inv[2]%M+C[i])%M;
}
inline void Div(int* A,int* B,int n,int m,int* D,int* R){
	for(int i=0;i<m;++i) rB[i]=B[m-i-1];
	for(int i=0;i<n;++i) rA[i]=A[n-i-1];
	int dG=n-m+1,L=1; for(;L<(dG<<1);L<<=1);
	for(int i=dG;i<L;++i) rA[i]=rB[i]=0;
	for(int i=0;i<L;++i) iB[i]=0;
	gInv(rB,iB,L);
	for(int i=dG;i<L;++i) iB[i]=0;
	NTT(iB,L,1);
	NTT(rA,L,1);
	for(int i=0;i<L;++i) rA[i]=(LL)rA[i]*iB[i]%M;
	NTT(rA,L,0);
	for(int i=0;i<dG;++i) D[i]=rA[dG-i-1];
	for(int i=0;i<dG;++i) rA[i]=D[i];
	for(int i=0;i<m;++i) rB[i]=B[i];
	for(;L<(m+dG);L<<=1);
	for(int i=dG;i<L;++i) rA[i]=0;
	for(int i=m;i<L;++i) rB[i]=0;
	NTT(rA,L,1);
	NTT(rB,L,1);
	for(int i=0;i<L;++i) rA[i]=(LL)rA[i]*rB[i]%M;
	NTT(rA,L,0);
	for(int i=0;i<L;++i) R[i]=(A[i]-rA[i]+M)%M;
}
inline void Ln(int* A,int* B,int n){
	for(int i=0;i<(n<<1);++i) iA[i]=dA[i]=0;
	gInv(A,iA,n);
	for(int i=1;i<n;++i) dA[i-1]=(LL)A[i]*i%M;
	NTT(dA,n<<1,1);
	NTT(iA,n<<1,1);
	for(int i=0;i<(n<<1);++i) dA[i]=(LL)iA[i]*dA[i]%M;
	NTT(dA,n<<1,0);
	*B=B[n]=0;
	for(int i=1;i<n;++i) B[i]=(LL)dA[i-1]*inv[i]%M,B[i+n]=0;
}
inline void Exp(int* A,int* B,int n){
	if(n==1){ *B=1; return; }
	Exp(A,B,n>>1);
	for(int i=0;i<(n<<1);++i) b[i]=0;
	Ln(B,b,n);
	for(int i=0;i<n;++i) b[i]=((LL)A[i]-b[i]+M)%M;
	*b=(*b+1)%M;
	NTT(B,n<<1,1);
	NTT(b,n<<1,1);
	for(int i=0;i<(n<<1);++i) B[i]=(LL)B[i]*b[i]%M;
	NTT(B,n<<1,0);
}
int F[N],G[N],n,H[N],m,k;	
int main(){
//	freopen("1.in","r",stdin);
	init(); scanf("%d%d",&n,&k); ++n;
	for(int i=0;i<n;++i) scanf("%d",F+i);
	for(m=1;m<n;m<<=1);
	Ln(F,G,m);
	for(int i=0;i<n;++i) G[i]=(LL)G[i]*k%M;
	memset(F,0,sizeof F);
	Exp(G,F,m);
	for(int i=0;i<n;++i) printf("%d ",F[i]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值