运算法则 | 算法 | 时间复杂度 |
---|---|---|
多项式乘法 | 快速傅里叶变换 | Θ ( 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 次多项式 A A A,求多项式 B B B,满足
B ( x ) ≡ ln A ( x ) ( m o d x n ) B(x) \equiv \ln A(x)\pmod{x^n} B(x)≡lnA(x)(modxn)
% 结果对 998244353 \texttt{998244353} 998244353 取模。
数据范围 n ⩽ 1 0 5 n \leqslant 10^5 n⩽105
% 一般地,对于函数 F ( x ) = x a F(x)=x^a F(x)=xa,其导函数为
F ′ ( x ) = a x a − 1 F'(x)=ax^{a-1} F′(x)=axa−1
% 一般地,对于函数 F ′ ( x ) = x a F'(x)=x^a F′(x)=xa,其不定积分为
∫ x a d x = 1 a + 1 x a + 1 = F ( x ) \int x^a\text{d}x=\frac1{a+1}x^{a+1}=F(x) ∫xadx=a+11xa+1=F(x) 简而言之,积分和求导互为逆运算。
令 G ( x ) ≡ ln x ( m o d x n ) G(x)≡\ln x\pmod{x^n} G(x)≡lnx(modxn) 则 B ( x ) ≡ G ( A ( x ) ) ( m o d x n ) B(x)≡G(A(x))\pmod{x^n} B(x)≡G(A(x))(modxn) 两边求导,根据复合函数求导法则,得
B ′ ( x ) ≡ G ′ ( A ( x ) ) A ′ ( x ) ≡ A ′ ( x ) A ( x ) ≡ A ′ ( x ) A − 1 ( x ) ( m o d x n ) \begin{aligned}B'(x)&≡G'(A(x))A'(x)≡\frac{A'(x)}{A(x)}≡{A'(x)}A^{-1}(x)\pmod{x^n}\\ \end{aligned} B′(x)≡G′(A(x))A′(x)≡A(x)A′(x)≡A′(x)A−1(x)(modxn) 求逆后积分回去就好。
外部封装式
#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+10){
t=(ll*)malloc((n+10)*sizeof (ll));
memset(t,0,(n+10)*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];
}
void dao(int n,poly &A,poly &B){
for(int i=1;i<n;i++)
B[i-1]=1ll*i*A[i]%mod;
B[n-1]=0;
}
void summa(int n,poly &a,poly &b){
for(int i=1;i<n;i++)
b[i]=1ll*a[i-1]*inv(i)%mod;
b[0]=0;
}
void ln(int n,poly &f,poly &g){
poly A,invs;
dao(n,f,A);inv(n,f,invs);
calc(n,n);NTT(A);NTT(invs);
for(int i=0;i<limit;i++)
A[i]=A[i]*invs[i]%mod;
NTT(A,true);summa(n,A,g);
}
poly x,y;
int n;
int main(){
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%lld",&x[i]),x[i]%=mod;
ln(n,x,y);
for(int i=0;i<n;i++)
printf("%lld ",y[i]%mod);
return 0;
}
内部封装式
#include
using namespace std;
#define ll long long
#define MAXN 4000010
const ll mod=998244353;
ll ksm(ll a,ll b){
ll ret=1;
while(b){
if(b&1) ret=ret*a%mod;
a=a*a%mod; b>>=1;
} return ret;
}
#define invt(x) (ksm(x,mod-2))
const ll G=3,inG=invt(G);
struct poly{
ll *t;
int limit,inmit,l;
poly(int n=0):limit(1),l(0),inmit(0),t(NULL){init(n);}
~poly(){
if(t) free(t);
limit=l=inmit=0;
}
ll& operator[](int x){return t[x];}
const ll& operator[](int x)const{return t[x];}
void init(int n){
if(n<=limit&&limit!=1) return;
if(t) free(t);
while(limit<n) limit<<=1,++l;
inmit=invt(limit);
t=(ll*)calloc(limit,sizeof(ll));
}
static const int maxn=MAXN;
void fnt(int type){
static int now=0,r[maxn];
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){
ll wn=ksm(type==1?G:inG,(mod-1)/(mid<<1));
for(int j=0,B=mid<<1;j<limit;j+=B){
ll w=1;
for(int k=0;k<mid;k++,w=w*wn%mod){
int x=t[j+k],y=t[j+k+mid]*w%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;
}
poly &operator*=(poly &x){
fnt(1);x.fnt(1);
for(int i=0;i<limit;i++)
t[i]=t[i]*x[i]%mod;
fnt(-1); return *this;
}
void inv(int n,poly &g)const{
g.init(n);
if(n==1) return g[0]=invt(t[0]),void();
int half=(n+1)>>1;
inv(half,g);
poly F(n+n),G(n+n);
for(int i=0;i<n;i++) F[i]=t[i];
for(int i=0;i<half;i++) G[i]=g[i];
F.fnt(1);G.fnt(1);
for(int i=0;i<G.limit;i++)
F[i]=G[i]*((2-F[i]*G[i]%mod+mod)%mod)%mod;
F.fnt(-1);
for(int i=0;i<n;i++) g[i]=F[i];
}
void diff(poly &g)const{
g.init(limit);
for(int i=1;i<limit;i++)
g[i-1]=t[i]*i%mod;
g[limit-1]=0;
}
void inte(poly &g)const{
g.init(limit);
for(int i=1;i<limit;i++)
g[i]=t[i-1]*invt(i)%mod;
g[0]=0;
}
void ln(int n,poly &g)const{
g.init(n);
poly diffs(n+n+1),invs(n+n+1);
diff(diffs);
inv(n,invs);
diffs*=invs;
diffs.inte(g);
}
}a,b;
int main(){
int n;
scanf("%d",&n);
a.init(n+n+2);b.init(n+n+2);
for(int i=0;i<n;i++)
scanf("%lld",&a[i]);
a.ln(n,b);
for(int i=0;i<n;i++)
printf("%lld ",b[i]);
return 0;
}