多项式全家桶(简单基础小清新版本)
形式幂级数的导数和积分
注意求导后积分会导致常数项信息丢失。
泰勒展开
对于函数 (G),考虑选取一点 (x_0),用关于 (x-x_0) 的多项式 (F(x)) 来拟合多项式 (G)。通俗地讲,就是用一个幂级数 (F(x)) 来拟合 (G(x))。
我们取 (x_0=0) 的特殊情形有麦克劳林公式
常见泰勒展开
牛顿迭代
给定幂级数 (B(x)),求另一个幂级数 (A(x)) 使得 (B(A(x))equiv 0pmod {x^n})。前提:(B(A(X))) 收敛。
设我们已经取得了 (frac{n}{2}) 时的答案,设为 (A_0(x))。我们有 (A(x)equiv A_0(x) pmod {x^{frac{n}{2}}}),平方一下就有 ((A(x)-A_0(x)equiv 0 pmod {x^n})。
考虑对 (B(A(x))) 泰勒展开,以 (A_0(x)) 为展开的点,然后带入上述式子,最终可以解得
形式幂级数乘法逆
给定幂级数 (G(x)),求幂级数满足 (G(x)F(x)equiv 1 pmod {x^n})。
设我们已经递归求得了 (F_0(x)),即 (frac{n}{2}) 时的答案。有两种做法。
-
平方法
[(F(x)-F_0(x))^2 equiv 0\ F^2(x)-2F(x)F_0(x)+F_0^2(x) equiv 0\ G(x)F^2(x)-2G(x)F(x)F_0(x)+GF_0^2(x) equiv 0\ F(x)-2F_0(x)+G(x)F_0^2(x)equiv 0\ f(x)equiv 2F_0(x) - G(x)F_0^2(x) ] -
牛顿迭代法
设 (H(F(x))=F(x)-G(x)-1),即求 (H(F(x))equiv 0 pmod x^n) 的 (F(x))。
[egin{aligned} F(x)&equiv F_0-frac{H(F_0(x))}{H'(F_0(x))}\ F(x)&equiv F_0-frac{G(x)F_0(x)-1}{frac{1}{F_0(x)}}\ &equiv 2F_0-G(x)F_0^2(x) end{aligned} ]
可以用 (O(nlog n)) 递归求解。
形式幂级数对数函数
给定幂级数 (G(x)),求幂级数满足 (F(x)equiv ln(G(x)) pmod {x^n})。
注意常数项为 0。
可以用 (O(log n)) 解决。
形式幂级数指数函数
给定幂级数 (G(x)),求幂级数 (F(x)) 使得 (F(x)equiv exp(G(x)) pmod {x^n})。
考虑牛顿迭代法。设 (H(F(x))=ln(F(x))-G(x))。则我们要求 (H(F(x))equiv 0pmod {x^n})。设我们已经求出 (frac{n}{2}) 的答案 (F_0(x))。
可以用 (O(nlog n)) 解决。
形式幂级数快速幂
给定幂级数 (G(x)),求幂级数 (F(x)equiv G^k(x) pmod{x^n})。
可以用 (O(nlog n)) 解决。
还可以用快速幂解决,常数会更小但是多一个 (log)。
形式幂级数开平方
给定幂级数 (G(x)),求幂级数 (F(x)equiv sqrt{G(x)} pmod {x^n})。设已经解决了 (frac{n}{2}) 的答案 (F_0(x)),然后我们考虑用平方法。
全家桶
namespace Poly {
const int mod=998244353,gg=3,ig=332748118;
int lim,r[N],a[N],b[N],c[N],d[N],e[N],h[N];
void init(int n) {
int l=0;
for(lim=1;lim<=n;lim<<=1,l++);
rep(i,0,lim) r[i]=(r[i>>1]>>1)|((i&1)<<l-1);
}
int pls(int x,int y) {return x+=y,x>=mod?x-mod:x;}
int mns(int x,int y) {return x-=y,x<0?x+=mod:x;}
int ksm(int x,int y,int ret=1) {
while(y) {
if(y%2) ret=ret*x%mod;
x=x*x%mod, y>>=1;
}
return ret;
}
void ntt(int *f,int lim,int t) {
rep(i,0,lim-1) if(i<r[i]) swap(f[i],f[r[i]]);
for(int len=1;len<lim;len<<=1) {
int dw=ksm(t>0?gg:ig,(mod-1)/(len*2));
for(int i=0;i<lim;i+=len*2) {
int w=1;
for(int j=0;j<len;j++,w=w*dw%mod) {
int x=f[i+j], y=w*f[i+j+len]%mod;
f[i+j]=pls(x,y), f[i+j+len]=mns(x,y);
}
}
}
if(t==-1) {
int iv=ksm(lim,mod-2);
rep(i,0,lim) f[i]=f[i]*iv%mod;
}
}
void _mul(int *f,int *g,int len) {
init(len);
ntt(f,lim,1), ntt(g,lim,1);
rep(i,0,lim-1) f[i]=f[i]*g[i]%mod;
ntt(f,lim,-1);
}
void _dev(int *f,int *g,int len) {
rep(i,1,len-1) f[i-1]=i*g[i]%mod; f[len-1]=0;
}
void _int(int *f,int *g,int len) {
rep(i,1,len-1) f[i]=ksm(i,mod-2)*g[i-1]%mod; f[0]=0;
}
void _inv(int *f,int *g,int len) {
if(len==1) {f[0]=ksm(g[0],mod-2); return;}
_inv(f,g,(len+1)/2);
init(len*2);
rep(i,0,len-1) c[i]=g[i]; rep(i,len,lim) c[i]=0;
ntt(c,lim,1), ntt(f,lim,1);
rep(i,0,lim-1) f[i]=mns(2,f[i]*c[i]%mod)*f[i]%mod;
ntt(f,lim,-1);
rep(i,len,lim-1) f[i]=0;
}
void _ln(int *f,int *g,int len) {
memset(a,0,sizeof(a)), memset(b,0,sizeof(b));
_dev(a,g,len), _inv(b,g,len);
_mul(a,b,len*2), _int(f,a,len);
rep(i,len,lim) f[i]=0;
}
void _exp(int *f,int *g,int len) {
if(len==1) {f[0]=1; return;}
_exp(f,g,(len+1)/2);
init(len*2);
rep(i,0,lim) d[i]=e[i]=0;
rep(i,0,len-1) d[i]=g[i];
_ln(e,f,len);
ntt(f,lim,1), ntt(d,lim,1), ntt(e,lim,1);
rep(i,0,lim-1) f[i]=(1-e[i]+d[i]+mod)%mod*f[i]%mod;
ntt(f,lim,-1); rep(i,len,lim-1) f[i]=0;
}
void _pow(int *f,int *g,int len,int k) {
memset(h,0,sizeof(h));
_ln(h,g,len);
rep(i,0,len-1) h[i]=h[i]*k%mod; rep(i,len,lim) h[i]=0;
_exp(f,h,len);
rep(i,len,lim) f[i]=0;
}
void _sqrt(int *f,int *g,int len) {
if(len==1) {f[0]=1; return;}
_sqrt(f,g,(len+1)/2);
init(len*2);
rep(i,0,lim) d[i]=e[i]=0;
_inv(e,f,len);
rep(i,0,len-1) d[i]=g[i];
ntt(f,lim,1), ntt(d,lim,1), ntt(e,lim,1);
rep(i,0,lim-1) f[i]=(d[i]+f[i]*f[i]%mod)%mod*e[i]%mod*ksm(2,mod-2)%mod;
ntt(f,lim,-1); rep(i,len,lim-1) f[i]=0;
}
}