多项式求逆元模板【NTT/拆系数FFT】

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
板题:

一. 洛谷4238

模998244353,NTT:

#include<cstdio>
#include<algorithm>
#define maxn 400005
#define LL long long
using namespace std;
const int mod = 998244353, G = 3;
LL wn,w;
inline LL ksm(LL a,LL b){
	LL s=1;
	for(;b;b>>=1,a=a*a%mod) if(b&1) s=s*a%mod;
	return s;
}
inline void change(LL *a,int len)
{
	for(int i=1,j=len/2,k;i<len-1;i++)
	{
		if(i<j) swap(a[i],a[j]);
		for(k=len/2;j>=k;j-=k,k>>=1);
		j+=k;
	}
}
inline void ntt(LL *a,int len,int flg)
{
	change(a,len);
	for(int i=2;i<=len;i<<=1)
	{
		if(flg==1) wn=ksm(G,(mod-1)/i);
		else wn=ksm(G,mod-1-(mod-1)/i);
		for(int j=0;j<len;j+=i)
		{
			w=1;
			for(int k=j;k<j+i/2;k++)
			{
				LL u=a[k],v=w*a[k+i/2]%mod;
				a[k]=(u+v)%mod,a[k+i/2]=(u-v+mod)%mod;
				w=w*wn%mod;
			}
		}
	}
	if(flg==-1){
		LL ni=ksm(len,mod-2);
		for(int i=0;i<len;i++) a[i]=a[i]*ni%mod;
	}
}
int n,len;
LL a[maxn],b[maxn],tmp[maxn];
inline void poly_inv(int n,LL *a,LL *b)
{
	if(n==1) {b[0]=ksm(a[0],mod-2);return;}
	poly_inv((n+1)>>1,a,b);
	len=1;while(len<n+n-1) len<<=1;
	for(int i=0;i<len;i++) tmp[i]=i<n?a[i]:0;
	ntt(tmp,len,1),ntt(b,len,1);
	for(int i=0;i<len;i++) {b[i]=(2-tmp[i]*b[i]%mod)*b[i]%mod;if(b[i]<0) b[i]+=mod;}
	ntt(b,len,-1);
	for(int i=n;i<len;i++) b[i]=0;
}
int main()
{
	scanf("%d",&n);
	for(int i=0;i<n;i++) scanf("%lld",&a[i]);
	poly_inv(n,a,b);
	for(int i=0;i<n;i++) printf("%lld%c",b[i],i==n-1?10:32);
}
二. 洛谷4239

模109+7,拆系数FFT(7次fft版本)

// luogu-judger-enable-o2
#include<cstdio>
#include<cmath>
#include<algorithm>
#define maxn 400005
#define LL long long
using namespace std;
const int mod = 1e9+7, M1 = 31623, M2 = 14122;
const double Pi = acos(-1);
struct complex
{
	double r,i;
	complex(double _r=0,double _i=0):r(_r),i(_i){}
	complex operator + (const complex &t)const{return complex(r+t.r,i+t.i);}
	complex operator - (const complex &t)const{return complex(r-t.r,i-t.i);}
	complex operator * (const complex &t)const{return complex(r*t.r-i*t.i,r*t.i+i*t.r);}
	complex conj(){return complex(r,-i);}
}w[20][maxn/2];
inline void change(complex *a,int len)
{
	for(int i=1,j=len/2,k;i<len-1;i++)
	{
		if(i<j) swap(a[i],a[j]);
		for(k=len/2;j>=k;j-=k,k>>=1);
		j+=k;
	}
}
inline void fft(complex *a,int len,int flg)
{
	change(a,len);
	for(int i=2,o=0;i<=len;i<<=1,o++)
		for(int j=0;j<len;j+=i)
			for(int k=j;k<j+i/2;k++)
			{
				complex u=a[k],v=(flg==1?w[o][k-j]:w[o][k-j].conj())*a[k+i/2];
				a[k]=u+v,a[k+i/2]=u-v;
			}
	if(flg==-1) for(int i=0;i<len;i++) a[i].r/=len;
}
inline void MTT(int *a,int *b,int n,int *ret)
{
	static complex A[maxn],B[maxn],C[maxn],D[maxn],rt[3][maxn];
	int len=1;while(len<n+n-1) len<<=1;
	for(int i=0;i<len;i++)
		if(i<n)
		{
			A[i]=a[i]/M1,B[i]=a[i]%M1;
			C[i]=b[i]/M1,D[i]=b[i]%M1;
		}
		else A[i]=B[i]=C[i]=D[i]=0;
	fft(A,len,1),fft(B,len,1),fft(C,len,1),fft(D,len,1);
	for(int i=0;i<len;i++) rt[2][i]=A[i]*C[i],rt[1][i]=A[i]*D[i]+B[i]*C[i],rt[0][i]=B[i]*D[i];
	fft(rt[0],len,-1),fft(rt[1],len,-1),fft(rt[2],len,-1);
	for(int i=0;i<n;i++) ret[i]=((llround(rt[0][i].r)%mod+llround(rt[1][i].r)*M1%mod)%mod+llround(rt[2][i].r)*M2%mod)%mod;
}
inline int ksm(int a,int b){
	int s=1;
	for(;b;b>>=1,a=1ll*a*a%mod) if(b&1) s=1ll*s*a%mod;
	return s;
}
int n,a[maxn],b[maxn],tmp[maxn];
inline void poly_inv(int n,int *a,int *b)
{
	if(n==1) {b[0]=ksm(a[0],mod-2);return;}
	poly_inv((n+1)>>1,a,b);
	MTT(a,b,n,tmp);
	MTT(b,tmp,n,tmp);
	for(int i=0;i<n;i++) b[i]=(2*b[i]%mod-tmp[i]+mod)%mod;
}
int main()
{
	scanf("%d",&n);int len=1;while(len<n+n-1) len<<=1;
	for(int i=2,o=0;i<=len;i<<=1,o++)
		for(int j=0;j<i/2;j++)
			w[o][j]=complex(cos(2*Pi*j/i),sin(2*Pi*j/i));
	for(int i=0;i<n;i++) scanf("%d",&a[i]);
	poly_inv(n,a,b);
	for(int i=0;i<n;i++) printf("%d%c",b[i],i==n-1?10:32);
}

FFT(4次fft版本)
算法解析可以看这两篇 ↓ \darr (膜拜大佬)
Miskcoo’s Space
Clin233

#include<cstdio>
#include<cmath>
#include<algorithm>
#define maxn 400005
#define LL long long
using namespace std;
const int mod = 1e9+7, M = (1<<15)-1;
const double Pi = acos(-1);
struct complex
{
	double r,i;
	complex(double _r=0,double _i=0):r(_r),i(_i){}
	complex operator + (const complex &t)const{return complex(r+t.r,i+t.i);}
	complex operator - (const complex &t)const{return complex(r-t.r,i-t.i);}
	complex operator * (const complex &t)const{return complex(r*t.r-i*t.i,r*t.i+i*t.r);}
	complex conj(){return complex(r,-i);}
}w[20][maxn/2];
inline void change(complex *a,int len)
{
	for(int i=1,j=len/2,k;i<len-1;i++)
	{
		if(i<j) swap(a[i],a[j]);
		for(k=len/2;j>=k;j-=k,k>>=1);
		j+=k;
	}
}
inline void fft(complex *a,int len,int flg)
{
	change(a,len);
	for(int i=2,o=0;i<=len;i<<=1,o++)
		for(int j=0;j<len;j+=i)
			for(int k=j;k<j+i/2;k++)
			{
				complex u=a[k],v=(flg==1?w[o][k-j]:w[o][k-j].conj())*a[k+i/2];
				a[k]=u+v,a[k+i/2]=u-v;
			}
	if(flg==-1) for(int i=0;i<len;i++) a[i].r/=len,a[i].i/=len;//注意这里的虚部也要除
}
inline void MTT(int *a,int *b,int n,int *ret)
{
	static complex A[maxn],B[maxn],rt[2][maxn],da,db,dc,dd;
	int len=1;while(len<n+n-1) len<<=1;
	for(int i=0;i<len;i++)
		if(i<n) A[i]=complex(a[i]>>15,a[i]&M),B[i]=complex(b[i]>>15,b[i]&M);
		else A[i]=B[i]=0;
	fft(A,len,1),fft(B,len,1);
	for(int i=0,j;i<len;i++)
	{
		j=(i==0?0:len-i);
		da=(A[i]+A[j].conj())*complex(0.5,0);
		db=(A[i]-A[j].conj())*complex(0,-0.5);
		dc=(B[i]+B[j].conj())*complex(0.5,0);
		dd=(B[i]-B[j].conj())*complex(0,-0.5);
		rt[0][i]=da*dc+db*dd*complex(0,1),rt[1][i]=db*dc+da*dd*complex(0,1);
	}
	fft(rt[0],len,-1),fft(rt[1],len,-1);
	for(int i=0;i<n;i++) ret[i]=((llround(rt[0][i].r)%mod<<30)+llround(rt[0][i].i)+((llround(rt[1][i].r)+llround(rt[1][i].i))%mod<<15))%mod;
}
inline int ksm(int a,int b){
	int s=1;
	for(;b;b>>=1,a=1ll*a*a%mod) if(b&1) s=1ll*s*a%mod;
	return s;
}
int n,a[maxn],b[maxn],tmp[maxn];
inline void poly_inv(int n,int *a,int *b)
{
	if(n==1) {b[0]=ksm(a[0],mod-2);return;}
	poly_inv((n+1)>>1,a,b);
	MTT(a,b,n,tmp);
	MTT(b,tmp,n,tmp);
	for(int i=0;i<n;i++) b[i]=(2*b[i]%mod-tmp[i]+mod)%mod;
}
int main()
{
	scanf("%d",&n);int len=1;while(len<n+n-1) len<<=1;
	for(int i=2,o=0;i<=len;i<<=1,o++)
		for(int j=0;j<i/2;j++)
			w[o][j]=complex(cos(2*Pi*j/i),sin(2*Pi*j/i));
	for(int i=0;i<n;i++) scanf("%d",&a[i]);
	poly_inv(n,a,b);
	for(int i=0;i<n;i++) printf("%d%c",b[i],i==n-1?10:32);
}

或者还有个优化版,change改成了用r[],预处理单位根改成O(n),空间小了很多

wlen=1;while(wlen<n+n-1) wlen<<=1;
for(int i=0;i<wlen;i++) w[i]=complex(cos(2*Pi*i/wlen),sin(2*Pi*i/wlen));
for(int i=0;i<len;i++) r[i]=(r[i>>1]>>1)|(i&1?len>>1:0);
int r[maxn],wlen;
inline void fft(complex *a,int len,int flg)
{
	for(int i=0;i<len;i++) if(i<r[i]) swap(a[i],a[r[i]]);
	for(int i=2;i<=len;i<<=1)
		for(int j=0,t=wlen/i;j<len;j+=i)//t=wlen/i
			for(int k=j,o=0;k<j+i/2;k++,o+=t)
			{
				complex u=a[k],v=(flg==1?w[o]:w[o].conj())*a[k+i/2];
				a[k]=u+v,a[k+i/2]=u-v;
			}
	if(flg==-1) for(int i=0;i<len;i++) a[i].r/=len,a[i].i/=len;//注意这里的虚部也要除
}

多项式求逆NTT最新版:

#include<bits/stdc++.h>
#define maxn 2200005
using namespace std;
const int mod = 998244353;
typedef vector<int> Poly;
inline int Pow(int a,int b){
	int s=1; for(;b;b>>=1,a=1ll*a*a%mod) if(b&1) s=1ll*s*a%mod;
	return s;
}
int w[maxn]={1},r[maxn];
inline int add(int a,int b){return (a+=b)>mod?a-mod:a;}
inline int dec(int a,int b){return (a-=b)<0?a+mod:a;}
void NTT(Poly &a,int len,int flg){
	a.resize(len);
	for(int i=0;i<len;i++) if(i<r[i]) swap(a[i],a[r[i]]);
	for(int i=2,l=1,G=flg==1?3:(mod+1)/3;i<=len;i<<=1,l<<=1){
		for(int k=l-2,wn=Pow(G,(mod-1)/i);k>=0;k-=2) w[k+1]=1ll*(w[k]=w[k>>1])*wn%mod;
		for(int j=0;j<len;j+=i) for(int k=j;k<j+l;k++){
			int u=a[k],v=1ll*w[k-j]*a[k+l]%mod;
			a[k]=add(u,v),a[k+l]=dec(u,v);
		}
	}
	if(flg==-1) for(int i=0,Inv=Pow(len,mod-2);i<len;i++) a[i]=1ll*a[i]*Inv%mod;
}
Poly Inv(Poly A,int n){
	Poly B(1,Pow(A[0],mod-2)),C;
	for(int k=2,len=4;k<n<<1;k=len,len<<=1){
		C=Poly(A.begin(),A.begin()+min(n,k));
		for(int i=0;i<len;i++) r[i]=r[i>>1]>>1|(i&1?len>>1:0);
		NTT(C,len,1),NTT(B,len,1);
		for(int i=0;i<len;i++) B[i]=1ll*B[i]*(2-1ll*C[i]*B[i]%mod+mod)%mod;
		NTT(B,len,-1),B.resize(min(n,k));
	}
	return B;
}
int n; Poly A;
int main()
{
	scanf("%d",&n),A.resize(n);
	for(int i=0;i<n;i++) scanf("%d",&A[i]);
	Poly B(Inv(A,n));
	for(int i=0;i<n;i++) printf("%d%c",B[i],i==n-1?10:32);
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值