多項式求逆
定義
設\(\displaystyle f(x) =\sum^{n-1}_{k=0}a_kx^k\)求\(g(x) =\sum^{n-1}_{k=0}b_kx^k\),使得
\(\displaystyle f(x)g(x)\equiv 1 (\mod x^n)\)
即\(\displaystyle f(x)g(x)\) 的前\(n\)項中只有常數項為\(1\),其余項均為\(0\),稱為
求\(f(x)\)模\(\displaystyle x^n\)的逆元,或簡稱求\(f(x)\)的逆元。
多項式可逆當且僅當其常數項可逆,證明見“方法一”\(\displaystyle b_0=\frac{1}{a_0}\)
方法1:分治FFT
從常數項出發,比較系數得\(\displaystyle b_0=\frac{1}{a_0}\)且\(\displaystyle \sum^k_{i=0}b_ia_{k-i}=0\)那么有
從而可以通過分治FFT 計算,時間復雜度\(\displaystyle \Theta(n \log^2 n)\)。
方法2:倍增法
考慮倍增,從局部的逆元(低次)出發,設\(\displaystyle g_0(x) = g(x) \mod x^k\),那么有
從而有
可以發現模的次數增加了,此時展開左邊可得
從而可以算出\(g(x)\)的前\(2k\)項。
通過攤還分析得時間復雜度為\(\displaystyle \Theta(n \log n)\)
實現
多項式求逆的實現不難,兩種方法都可以,但第二種更快,也更容易實現,故此處以方法二為例。
我們每次遞歸算出模\(\displaystyle x^{\frac{n}{2}}\)的逆元,記為\(g_0\),再套用\(\displaystyle g(x)\equiv g_0(x)(2-f(x)g_0(x))(\mod x^{2k})\),用FFT計算即可
void solve(LL *a,LL *b,int p){/*a:seq,b:inv*/
if(p==1){
b[0]=fpm(a[0],MOD-2);
return;
}
solve(a,b,(p+1)>>1);
lim=1; L=0;
while(lim<(p<<1)){
lim<<=1;
L++;
}
for(int i=0;i<lim;i++)
rev[i]=rev[i>>1]>>1|(1&i)<<(L-1);
for(int i=0;i<p;i++)
tmp[i]=a[i];
for(int i=p;i<lim;i++)
tmp[i]=0;
NTT(tmp,1); NTT(b,1);
for(int i=0;i<lim;i++)
b[i]=(2-b[i]*tmp[i]%MOD+MOD)%MOD*b[i]%MOD;
NTT(b,-1);
for(int i=p;i<lim;i++)
b[i]=0;
}
例題
[P4238 【模板】多項式求逆](%3Ca href="https://www.luogu.org/problem/P4238"%3Ehttps://www.luogu.org/problem/P4238%3C/a%3E)
這是純粹的模板題,直接給代碼
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int INF=1e9+7,MAXN=3e6+10/*Min:2^20+10*/;
const LL G=3,MOD=998244353,INV=332748118;
inline LL fpm(LL base,LL p){
LL ret=1;
while(p){
if(p&1)
ret=ret*base%MOD;
base=base*base%MOD;
p>>=1;
}
return ret%MOD;
}
int N,M,lim=1,L,rev[MAXN];
inline void NTT(LL *a,int type){
for(int i=0;i<lim;i++)
if(i<rev[i])
swap(a[i],a[rev[i]]);
for(int mid=1;mid<lim;mid<<=1){
int len=mid<<1/*n*/;
LL Wn=fpm(G,(MOD-1)/len);
for(int j=0;j<lim;j+=len){
LL w=1,t1,t2;
for(int k=0;k<mid;k++){
t1=a[j+k],t2=w*a[j+k+mid]%MOD;
a[j+k]=(t1+t2)%MOD;
a[j+k+mid]=(t1-t2+MOD)%MOD;
w=w*Wn%MOD;
}
}
}
if(type==-1){
LL lim_inv=fpm(lim,MOD-2);
reverse(a+1,a+lim);
for(int i=0;i<lim;i++)
a[i]=a[i]*lim_inv%MOD;
}
}
LL tmp[MAXN];
void solve(LL *a,LL *b,int p){/*a:seq,b:inv*/
if(p==1){
b[0]=fpm(a[0],MOD-2);
return;
}
solve(a,b,(p+1)>>1);
lim=1; L=0;
while(lim<(p<<1)){
lim<<=1;
L++;
}
for(int i=0;i<lim;i++)
rev[i]=rev[i>>1]>>1|(1&i)<<(L-1);
for(int i=0;i<p;i++)
tmp[i]=a[i];
for(int i=p;i<lim;i++)
tmp[i]=0;
NTT(tmp,1); NTT(b,1);
for(int i=0;i<lim;i++)
b[i]=(2-b[i]*tmp[i]%MOD+MOD)%MOD*b[i]%MOD;
NTT(b,-1);
for(int i=p;i<lim;i++)
b[i]=0;
}
LL a[MAXN],b[MAXN];
int main(){
scanf("%d",&N);
for(int i=0;i<N;i++){
scanf("%lld",a+i);
a[i]=(a[i]+MOD)%MOD;
}
solve(a,b,N);
for(int i=0;i<N;i++)
printf("%lld ",b[i]);
return 0;
}
[P4239 【模板】多項式求逆(加強版)](%3Ca href="https://www.luogu.org/problem/P4239"%3Ehttps://www.luogu.org/problem/P4239%3C/a%3E)
這道題是在任意模數下的多項式求逆,需要將上一個板子的NTT改成MTT