多项式求逆

多项式求逆


\(A(x)\)\(\%x^{n}\) 意义下的逆元 \(B(x)\)

首先求出 \(A(x)\)\(\%x^{\lceil \frac{n}{2} \rceil}\) 意义下的逆元 \(C(x)\),即 $A(x)C(x)=1 $ \((\%x^{\lceil \frac{n}{2} \rceil})\)

移项得 \(A(x)C(x)-1 = 0\) \((\%x^{\lceil \frac{n}{2} \rceil})\)

两边平方 \(A^{2}(x)C^{2}(x)-2A(x)C(x)+1=0\) \((\% x^{n})\)

然后两边同时 \(\times B(x)\)\(A(x)C^{2}(x)-2C(x)+B(x)=0\) \((\% x^{n})\)

然后得到 \(B(x)=2C(x)-A(x)C^{2}(x)\) \((\% x^{n})\)

\(B(x)=C(x)(2-A(x)C(x))\)

然后递归求解即可

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=400009;

const int mm=998244353;

long long Ksm(long long a,int p){
	long long ret=1;
	for(;p;p>>=1,a=a*a%mm){
		if(p&1)ret=ret*a%mm;
	}
	return ret;
}

int rev[maxn];
void NTT(long long *arr,int n,int f){
	int b=0;
	for(int len=1;len<n;len<<=1)++b;
	for(int i=0;i<n;++i)rev[i]=(rev[i>>1]>>1)|((i&1)<<(b-1));
	for(int i=0;i<n;++i)if(i<rev[i])swap(arr[i],arr[rev[i]]);
	
	for(int k=1;k<n;k<<=1){
		int p=k+k;
		long long wn=Ksm(3LL,(mm-1)/p);
		if(f==-1)wn=Ksm(wn,mm-2);
		
		for(int i=0;i<n;i+=p){
			long long w=1;
			for(int j=0;j<k;++j,w=w*wn%mm){
				long long x=arr[i+j],y=arr[i+j+k]*w%mm;
				arr[i+j]=(x+y)%mm;
				arr[i+j+k]=(x-y+mm)%mm;
			}
		}
	}
	
	if(f==-1){
		long long inv=Ksm(n*1LL,mm-2);
		for(int i=0;i<n;++i)arr[i]=arr[i]*inv%mm;
	}
}

int n;

long long A[maxn],B[maxn],C[maxn];

void DivCon(int dg){
	if(dg==1){
		B[0]=Ksm(A[0],mm-2);
		return;
	}
	
	DivCon((dg+1)>>1);
	
	int len=1;
	for(len=1;len<(dg<<1);len<<=1);
	
	for(int i=0;i<dg;++i)C[i]=A[i];
	for(int i=dg;i<len;++i)C[i]=0;
	NTT(B,len,1);
	NTT(C,len,1);
	for(int i=0;i<len;++i)B[i]=B[i]*(2-B[i]*C[i]%mm+mm)%mm;
	
	NTT(B,len,-1);
	
	for(int i=dg;i<len;++i)B[i]=0;
}

int main(){
	scanf("%d",&n);
	for(int i=0;i<n;++i)scanf("%lld",&A[i]);
	
	DivCon(n);
	
	for(int i=0;i<n;++i)printf("%lld ",B[i]);
	printf("\n");
	
	return 0;
}

luoguP4721 分治FFT

已知\(f(0)=1\)

\(f(i)= \sum_{j=0}^{i-1}f(j)g(i-j)\)

\(f\)

分治FFT的做法

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=400009;

const int mm=998244353;

long long Ksm(long long a,int p){
	long long ret=1;
	for(;p;p>>=1,a=a*a%mm){
		if(p&1)ret=ret*a%mm;
	}
	return ret;
}

int rev[maxn];
void NTT(long long *arr,int n,int f){
	int b=0;
	for(int len=1;len<n;len<<=1)++b;
	for(int i=0;i<n;++i)rev[i]=(rev[i>>1]>>1)|((i&1)<<(b-1));
	for(int i=0;i<n;++i)if(i<rev[i])swap(arr[i],arr[rev[i]]);
	
	for(int k=1;k<n;k<<=1){
		int p=k+k;
		long long wn=Ksm(3LL,(mm-1)/p);
		if(f==-1)wn=Ksm(wn,mm-2);
		
		for(int i=0;i<n;i+=p){
			long long w=1;
			for(int j=0;j<k;++j,w=w*wn%mm){
				long long x=arr[i+j],y=arr[i+j+k]*w%mm;
				arr[i+j]=(x+y)%mm;
				arr[i+j+k]=(x-y+mm)%mm;
			}
		}
	}
	
	if(f==-1){
		long long inv=Ksm(n*1LL,mm-2);
		for(int i=0;i<n;++i)arr[i]=arr[i]*inv%mm;
	}
}

long long PolyA[maxn],PolyB[maxn];

int n;

long long f[maxn]={0},g[maxn]={0};

void DivFFT(int l,int r){
	if(l==r){
		return;
	}
	
	int mid=(l+r)>>1;
	DivFFT(l,mid);
	
	int p1=0,p2=0,len=0;
	for(int i=l;i<=mid;++i)PolyA[p1++]=f[i];
	for(int i=1;i<=r-l;++i)PolyB[p2++]=g[i];
	for(len=1;len<=(p1+p2-2);len<<=1);
	for(int i=p1;i<len;++i)PolyA[i]=0;
	for(int i=p2;i<len;++i)PolyB[i]=0;
	NTT(PolyA,len,1);
	NTT(PolyB,len,1);
	for(int i=0;i<len;++i)PolyA[i]=PolyA[i]*PolyB[i]%mm;
	NTT(PolyA,len,-1);
	for(int i=mid+1;i<=r;++i){
		f[i]=(f[i]+PolyA[p1-2+i-mid])%mm;
	}
	
	DivFFT(mid+1,r);
}

int main(){
	scanf("%d",&n);
	for(int i=1;i<n;++i)scanf("%lld",&g[i]);
	
	f[0]=1;
	DivFFT(0,n-1);
	
	for(int i=0;i<n;++i)printf("%lld ",f[i]);
	printf("\n");
	
	return 0;
}

多项式求逆的做法

\(g(0)=0\),则\(f(i) =\sum_{j=0}^{i} f(j)g(i-j)\)

那么 \(f*g + 1=f\)

\(f(1-g)=1\)

\(f= \frac{1}{1-g}\)

\(\frac{1}{1-g}\)\(\%x^{n+1}\)下求逆即可

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=400009;

const int mm=998244353;

long long Ksm(long long a,int p){
	long long ret=1;
	for(;p;p>>=1,a=a*a%mm){
		if(p&1)ret=ret*a%mm;
	}
	return ret;
}

int rev[maxn];
void NTT(long long *arr,int n,int f){
	int b=0;
	for(int len=1;len<n;len<<=1)++b;
	for(int i=0;i<n;++i)rev[i]=(rev[i>>1]>>1)|((i&1)<<(b-1));
	for(int i=0;i<n;++i)if(i<rev[i])swap(arr[i],arr[rev[i]]);
	
	for(int k=1;k<n;k<<=1){
		int p=k+k;
		long long wn=Ksm(3LL,(mm-1)/p);
		if(f==-1)wn=Ksm(wn,mm-2);
		
		for(int i=0;i<n;i+=p){
			long long w=1;
			for(int j=0;j<k;++j,w=w*wn%mm){
				long long x=arr[i+j],y=arr[i+j+k]*w%mm;
				arr[i+j]=(x+y)%mm;
				arr[i+j+k]=(x-y+mm)%mm;
			}
		}
	}
	
	if(f==-1){
		long long inv=Ksm(n*1LL,mm-2);
		for(int i=0;i<n;++i)arr[i]=arr[i]*inv%mm;
	}
}

int n;

long long A[maxn],B[maxn],C[maxn];

void PolyInv(int dg){
	if(dg==1){
		B[0]=Ksm(A[0],mm-2);
		return;
	}
	
	PolyInv((dg+1)>>1);
	
	int len=1;
	for(len=1;len<(dg<<1);len<<=1);
	
	for(int i=0;i<dg;++i)C[i]=A[i];
	for(int i=dg;i<len;++i)C[i]=0;
	NTT(B,len,1);
	NTT(C,len,1);
	for(int i=0;i<len;++i)B[i]=B[i]*(2-B[i]*C[i]%mm+mm)%mm;
	
	NTT(B,len,-1);
	
	for(int i=dg;i<len;++i)B[i]=0;
}

int main(){
	scanf("%d",&n);
	for(int i=1;i<n;++i)scanf("%lld",&A[i]);
	A[0]=1;
	for(int i=1;i<n;++i)A[i]=(mm-A[i])%mm;
	PolyInv(n);
	
	for(int i=0;i<n;++i)printf("%lld ",B[i]);
	printf("\n");
	
	return 0;
}

\(f(n)=\sum_{i=1}^{n-1}f(i)f(n-i)\) 怎么分治FFT???

posted @ 2018-06-19 17:28  ws_zzy  阅读(146)  评论(0编辑  收藏  举报