运算法则 | 算法 | 时间复杂度 |
---|---|---|
多项式乘法 | 快速傅里叶变换 | Θ ( n log 2 n ) \Theta(n\log_2 n) Θ(nlog2n) |
多项式求逆 | 倍增+快速数论变换 | Θ ( n log 2 n ) \Theta(n\log_2 n) Θ(nlog2n) |
多项式对数函数 | 求导+积分 | Θ ( n log 2 n ) \Theta(n\log_2 n) Θ(nlog2n) |
多项式指数函数 | 泰勒展开+牛顿迭代 | Θ ( n log 2 n ) \Theta(n\log_2 n) Θ(nlog2n) |
分治FFT卷积 | 分治FFT/多项式求逆 | Θ ( n log 2 2 n ) / Θ ( n log 2 n ) \Theta(n\log_2^2 n)/\Theta(n\log_2 n) Θ(nlog22n)/Θ(nlog2n) |
% 给定一个 n − 1 n-1 n−1 次多项式 F F F,求 G G G,满足 F ∗ G ≡ 1 ( m o d x n ) F*G≡1\pmod{x^n} F∗G≡1(modxn)
% 结果对 998244353 998244353 998244353 取模。
% 数据范围 1 ⩽ n ⩽ 1 0 5 , 0 ⩽ a i ⩽ 1 0 9 1\leqslant n\leqslant 10^5,0\leqslant a_i\leqslant 10^9 1⩽n⩽105,0⩽ai⩽109
% 首先你要清楚多项式模 x n x^{n} xn 指的是仅保留这个多项式的 0 ∼ n − 1 0\sim n-1 0∼n−1次项。
令 G ′ G' G′ 满足 F ∗ G ′ ≡ 1 ( m o d x ⌈ n 2 ⌉ ) F*G'≡1\pmod{x^{\small \lceil\frac n2\rceil}} F∗G′≡1(modx⌈2n⌉) 则有
G ′ − G ≡ 0 ( m o d x ⌈ n 2 ⌉ ) G'-G≡0\pmod{x^{\small \lceil\frac n2\rceil}} G′−G≡0(modx⌈2n⌉)
% 平方,得
( G ′ − G ) 2 ≡ 0 ( m o d x n ) (G'-G)^2≡0\pmod{x^n} (G′−G)2≡0(modxn)
% 展开
( G ′ ) 2 − 2 G G ′ + G 2 ≡ 0 ( m o d x n ) (G')^2-2GG'+G^2≡0\pmod{x^n} (G′)2−2GG′+G2≡0(modxn)
% 两边同时乘 F F F,得
F ( G ′ ) 2 − 2 G ′ + G ≡ 0 ( m o d x n ) F(G')^2-2G'+G≡0\pmod{x^n} F(G′)2−2G′+G≡0(modxn)
% 移项
G ≡ 2 G ′ − F ( G ′ ) 2 ≡ G ′ ( 2 − F G ′ ) ( m o d x n ) G≡2G'-F(G')^2≡G'(2-FG')\pmod{x^n} G≡2G′−F(G′)2≡G′(2−FG′)(modxn)
% 运用 FNT \text{FNT} FNT 可以将多项式乘法的时间复杂度降低到 Θ ( n log 2 n ) \Theta(n\log_2 n) Θ(nlog2n)。可以得到递推式
T ( n ) = T ( n 2 ) + Θ ( n log 2 n ) T(n)=T\left(\frac n2\right)+\Theta(n\log_2n) T(n)=T(2n)+Θ(nlog2n) 根据主定理1,可得
T ( n ) = Θ ( n log 2 n ) T(n)=\Theta(n\log_2 n) T(n)=Θ(nlog2n) 通俗地讲,它每次倍增,而非每次分治,因而时间复杂度不会累乘,而是累加。
外部封装式:
#include
using namespace std;
#define ll long long
#define maxn 400010
const ll mod=(119<<23)+1,G=3,inv_G=332748118;
ll pow_t(ll a,ll b){
ll ret=1;
for(a%=mod;b;a=a*a%mod,b>>=1)
if(b&1) ret=ret*a%mod;
return ret;
} ll inv(ll x){return pow_t(x,mod-2);}
struct poly{
private:ll q_lens;
public:ll *t;
poly(int n=maxn):q_lens(n){
t=(ll*)malloc(n*sizeof (ll));
memset(t,0,n*sizeof (ll));
} ~poly(){free(t);}
void sets(){memset(t,0,q_lens*sizeof (ll));}
ll &operator[](const int &x){return t[x];}
};
int limit,l,r[maxn];
void calc(int n,int m){
for(limit=1,l=0;limit<=n+m;limit<<=1,++l);
for(int i=0;i<limit;i++) r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
}
void NTT(poly &t,bool un=false){
for(int i=0;i<limit;i++)
if(i<r[i]) swap(t[i],t[r[i]]);
for(int mid=1;mid<limit;mid<<=1){
ll wn=pow_t((un?inv_G:G),(mod-1)/(mid<<1));
for(int j=0;j<limit;j+=(mid<<1)){
ll w=1;
for(int k=0;k<mid;++k,w=w*wn%mod){
ll x=t[j+k],y=w*t[j+k+mid]%mod;
t[j+k]=(x+y)%mod;
t[j+k+mid]=(x-y+mod)%mod;
}
}
} ll invs=inv(limit);
if(un) for(int i=0;i<limit;i++)
t[i]=t[i]*invs%mod;
}
void inv(int n,poly &f,poly &g){
if(n==1) return(void)(g[0]=inv(f[0]));
inv((n+1)>>1,f,g); calc(n,n);
poly a(limit),b(limit);
for(int i=0;i<n;i++) a[i]=f[i],b[i]=g[i];
NTT(a); NTT(b);
for(int i=0;i<limit;i++)
b[i]=b[i]*((2ll-a[i]*b[i]%mod+mod)%mod)%mod;
NTT(b,true);
for(int i=0;i<n;i++) g[i]=b[i];
}
poly x,y;
int n;
int main(){
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%lld",&x[i]),x[i]%=mod;
inv(n,x,y);
for(int i=0;i<n;i++)
printf("%lld ",y[i]%mod);
return 0;
}
内部封装式(编译时间较长)
#include
#include
#include
using namespace std;
#define MAXN 4000010
const long long mod=998244353,G=3,inG=332748118;
long long pows(long long a,long long b){
long long ret=1;
while(b){
if(b&1) ret=ret*a%mod;
b>>=1;a=a*a%mod;
} return ret;
}
class poly{
public:
static const int maxn=MAXN;
long long *t;
int limit,l,inmit;
long long& operator[](int x){return t[x];}
const long long& operator[](int x)const{return t[x];}
poly(int n=0):t(NULL),limit(1),l(0){init(n);}
void init(int n){
if(limit>=n) return;
if(t) free(t);
while(limit<n) limit<<=1,++l;
inmit=pows(limit,mod-2);
t=(long long*)calloc(limit+5,sizeof(long long));
}
void fnt(int type){
static long long r[MAXN];
static int now=0;
if(now!=limit&&(now=limit))
for(int i=1;i<limit;i++)
r[i]=((r[i>>1]>>1)|((i&1)<<(l-1)));
for(int i=1;i<limit;i++)
if(i<r[i]) swap(t[i],t[r[i]]);
for(int mid=1;mid<limit;mid<<=1){
long long wn=pows(type==1?G:inG,(mod-1)/(mid<<1));
for(int j=0,B=(mid<<1);j<limit;j+=B){
long long w=1;
for(int k=0;k<mid;k++,w=w*wn%mod){
int x=t[j+k],y=w*t[j+k+mid]%mod;
t[j+k]=(x+y)%mod;t[j+k+mid]=(x-y+mod)%mod;
}
}
} if(type==-1) for(int i=0;i<limit;i++)
t[i]=t[i]*inmit%mod;
}
void operator*=(poly &x){
fnt(1);x.fnt(1);
for(int i=0;i<limit;i++)
t[i]=t[i]*x[i]%mod;
fnt(-1);
}
void inv(int n,poly &g)const{
g.init(n+n);
if(n==1) return(void)(g[0]=pows(t[0],mod-2));
inv((n+1)>>1,g);
poly a(n+n),b(n+n);
for(int i=0;i<n;i++) a[i]=t[i],b[i]=g[i];
a.fnt(1); b.fnt(1);
for(int i=0;i<a.limit;i++)
b[i]=b[i]*((2ll-a[i]*b[i]%mod+mod)%mod)%mod;
b.fnt(-1);
for(int i=0;i<n;i++) g[i]=b[i];
}
}a,b;
int n;
int main(){
scanf("%d",&n);
a.init(n);
for(int i=0;i<n;i++)
scanf("%lld",&a[i]);
a.inv(n,b);
for(int i=0;i<n;i++)
printf("%lld ",b[i]);
return 0;
}
% 根据 T ( n ) = T ( n 2 ) + Θ ( n log 2 n ) T(n)=T\left(\dfrac n2\right)+\Theta(n\log_2 n) T(n)=T(2n)+Θ(nlog2n) 因而 f ( n ) = Θ ( n log 2 n ) > Θ ( n l o g 2 1 ) = Θ ( 1 ) f(n)=\Theta(n\log_2 n)>\Theta(n^{log_2 1})=\Theta(1) f(n)=Θ(nlog2n)>Θ(nlog21)=Θ(1),故 T ( n ) = f ( n ) = Θ ( n log 2 n ) T(n)=f(n)=\Theta(n\log_2 n) T(n)=f(n)=Θ(nlog2n) ↩︎