浅谈多项式
1.FFT/NTT
略。
2.多项式求逆
求 \(F^{-1}(x)\times F(x)=1\pmod {x^n}\)
我们假设已经求出了 \(F_*^{-1}(x)\times F(x)=1\pmod {x^{n/2}}\) (\(n/2\) 向上取整)
而 \(F^{-1}(x)\times F(x)=1\pmod {x^{n/2}}\)
\(F_*^{-1}(x)-F^{-1}(x)=0\pmod {x^{n/2}}\)
\(F_*^{-2}(x)+F^{-2}(x)-2F_*^{-1}(x)F^{-1}(x)=0\pmod {x^n}\)
\(F_*^{-2}(x)F(x)+F^{-1}(x)-2F_*^{-1}(x)=0\pmod {x^n}\)
\(F^{-1}(x)=(2-F_*^{-1}(x)\times F(x))F_*^{-1}(x)\)
递归 +NTT 即可。
边界: \(n=1\) 时,为 \(F(0)\) 的逆元。
3.多项式开根
求 \(G^2(x)-F(x)=0\pmod {x^n}\)。
设 \(G_*^2(x)-F(x)=0 \pmod {x^{n/2}}\)
而 \(G(x)-G_*(x)=0\pmod {x^{n/2}}\).
\(G^2(x)+G^2_*(x)-2G(x)G_*(x)=0\pmod {x^n}\)
\(F(x)+G_*^2(x)-2G(x)G_*(x)\pmod {x^n}\)
\(G(x)=\dfrac{F(x)+G_*^2(x)}{2G_*(x)}\pmod {x^n}\).
同样地,考虑倍增求解即可。
4.多项式求 ln
求 \(G(x)=\ln F(x) \pmod {x^n}\)
\(G'(x)=\ln' (F(x))\times F'(x)\).
因为 \(\ln'x =\frac{1}{x}\),有 \(G'(x)=\dfrac{F'(x)}{F(x)}\pmod {x^n}\).
在把 \(G\) 积分回去即可。
5.多项式求 exp
求 \(G(x)=e^{F(x)} \pmod {x^n}\)
证明超出笔者能力。倍增:
若 \(G_*(x)=e^{F(x)}\pmod {x^{n/2}}\).
那么 \(G(x)=G_*(x)(1-\ln G_*(x)+F(x))\pmod {x^n}\).
6.多项式快速幂
考虑先 \(\ln\),翻 \(k\) 倍后 \(\exp\) 回去。
*7.分治 NTT
半在线卷积,求 \(f_i=\sum_{j=1}^nf_{i-j}g_j\).
考虑分治,对于当前分治到求 \(f_l\sim f_r\),
我们先求左边的 \(f_l\sim f_{mid}\),然后对 \(g_0\sim g_{r-l+1}\) 做个卷积,
把贡献加到 \(f_{mid+1}\sim f_r\) 即可。
事实上有求逆的解法,我们发现 \(F=1+F\times G\).
所以 \((1-G)\times F=1\),那么 \(F=(1-G)^{-1}\).
code
#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int N=8e5+10;
const int p=998244353,g=3,gi=332748118;
const int inv2=499122177;
int tr[N],a[N],b[N],c[N],d[N];
int fpow(int x,int k) {
int res=1;
for(; k; k>>=1,x=1ll*x*x%p)
if(k&1) res=1ll*res*x%p;
return res;
}
void ntt(int *F,int n,int op) {
for(int i=1; i<n; i++)
tr[i]=(tr[i>>1]>>1)|((i&1)?n>>1:0);
for(int i=1; i<n; i++) if(i<tr[i]) swap(F[i],F[tr[i]]);
for(int len=2; len<=n; len<<=1)
for(int i=0; i<n; i+=len) {
int w=fpow(op==1?g:gi,(p-1)/len),buf=1;
for(int j=i; j<i+len/2; j++) {
int tmp=1ll*F[j+len/2]*buf%p;
F[j+len/2]=(F[j]-tmp+p)%p;
F[j]=(F[j]+tmp)%p;
buf=1ll*buf*w%p;
}
}
if(op!=1) {
int tmp=fpow(n,p-2);
for(int i=0; i<n; i++) F[i]=1ll*F[i]*tmp%p;
}
}
void mul(int *F,int *G,int n,int *res) {
ntt(F,n,1); ntt(G,n,1);
for(int i=0; i<n; i++) res[i]=1ll*F[i]*G[i]%p;
ntt(res,n,-1);
}
void getinv(int *F,int *res,int n) {
if(n==1) return res[0]=fpow(F[0],p-2),void();
getinv(F,res,(n+1)>>1);
int lim=1;
while(lim<(n<<1)) lim<<=1;
for(int i=0; i<n; i++) c[i]=F[i];
for(int i=n; i<lim; i++) c[i]=0;
ntt(c,lim,1); ntt(res,lim,1);
for(int i=0; i<lim; i++) res[i]=1ll*(2-1ll*c[i]*res[i]%p+p)*res[i]%p;
ntt(res,lim,-1);
for(int i=n; i<lim; i++) res[i]=0;
for(int i=0; i<lim; i++) c[i]=0;
}
void getsqrt(int *F,int *res,int n) {
if(n==1) return res[0]=1,void();
getsqrt(F,res,(n+1)>>1);
int lim=1;
while(lim<(n<<1)) lim<<=1;
for(int i=0; i<lim; i++) d[i]=0;
getinv(res,d,n);
for(int i=0; i<n; i++) c[i]=F[i];
for(int i=n; i<lim; i++) c[i]=0;
ntt(c,lim,1); ntt(d,lim,1); ntt(res,lim,1);
for(int i=0; i<lim; i++) res[i]=1ll*(res[i]+1ll*c[i]*d[i]%p)%p*inv2%p;
ntt(res,lim,-1);
for(int i=n; i<lim; i++) res[i]=0;
}
void getDao(int *F,int *res,int n) {
for(int i=1; i<n; i++) res[i-1]=1ll*F[i]*i%p;
res[n-1]=0;
}
void getjf(int *F,int *res,int n) {
for(int i=1; i<n; i++) res[i]=1ll*F[i-1]*fpow(i,p-2)%p;
res[0]=0;
}
void getln(int *F,int *res,int n) {
int lim=1;
while(lim<(n<<1)) lim<<=1;
for(int i=0; i<lim; i++) res[i]=0;
getinv(F,res,n); getDao(F,c,n);
ntt(res,lim,1); ntt(c,lim,1);
for(int i=0; i<lim; i++) c[i]=1ll*res[i]*c[i]%p;
ntt(c,lim,-1);
getjf(c,res,n);
for(int i=0; i<lim; i++) c[i]=0;
}
void getexp(int *F,int *res,int n) {
if(n==1) return res[0]=1,void();
getexp(F,res,(n+1)>>1);
getln(res,b,n);
int lim=1;
while(lim<(n<<1)) lim<<=1;
b[0]=(F[0]-b[0]+p+1)%p;
for(int i=1; i<n; i++) b[i]=(F[i]-b[i]+p)%p;
for(int i=n; i<lim; i++) b[i]=0;
ntt(b,lim,1); ntt(res,lim,1);
for(int i=0; i<lim; i++) res[i]=1ll*res[i]*b[i]%p;
ntt(res,lim,-1);
for(int i=n; i<lim; i++) res[i]=0;
}
void getpow(int *F,int *res,int n,int k) {
getln(F,a,n);
for(int i=0; i<n; i++) a[i]=1ll*a[i]*k%p;
getexp(a,res,n);
}