多项式全家桶

多项式全家桶(简单基础小清新版本)

形式幂级数的导数和积分

\[F'(x)=\sum_{i=0}^{\infty} (i+1) a_{i+1}x^i\\ \int F(x) = (\sum_{i=1}^{\infty} \frac{a_{i-1}}{i} x^i) +C \]

注意求导后积分会导致常数项信息丢失。

泰勒展开

对于函数 \(G\),考虑选取一点 \(x_0\),用关于 \(x-x_0\) 的多项式 \(F(x)\) 来拟合多项式 \(G\)。通俗地讲,就是用一个幂级数 \(F(x)\) 来拟合 \(G(x)\)

\[F(x)=\sum_{i=0}^{\infty} \frac{G^{(i)} (x_0)}{i!} (x-x_0)^i \]

我们取 \(x_0=0\) 的特殊情形有麦克劳林公式

\[F(x)=\sum_{i=0}^{\infty} \frac{G^(i)(0)}{i!} x^i \]

常见泰勒展开

\[e^x=\sum_{i=0}^{\infty} \frac{x^i}{i!}\\ \ln (x+1)=\sum_{i=0}^{\infty} (-1)^{i-1} \frac{x^i}{i} \]

牛顿迭代

给定幂级数 \(B(x)\),求另一个幂级数 \(A(x)\) 使得 \(B(A(x))\equiv 0\pmod {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)\) 为展开的点,然后带入上述式子,最终可以解得

\[A(x)\equiv A_0(x) - \frac{B(A_0(x))}{B'(A_0(x))} \pmod {x^n} \]

形式幂级数乘法逆

给定幂级数 \(G(x)\),求幂级数满足 \(G(x)F(x)\equiv 1 \pmod {x^n}\)

设我们已经递归求得了 \(F_0(x)\),即 \(\frac{n}{2}\) 时的答案。有两种做法。

  1. 平方法

    \[(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) \]

  2. 牛顿迭代法

    \(H(F(x))=F(x)-G(x)-1\),即求 \(H(F(x))\equiv 0 \pmod x^n\)\(F(x)\)

    \[\begin{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(n\log n)\) 递归求解。

形式幂级数对数函数

给定幂级数 \(G(x)\),求幂级数满足 \(F(x)\equiv \ln(G(x)) \pmod {x^n}\)

\[\begin{aligned} F(x) &\equiv \ln(G(x))\\ F'(x) &\equiv \frac{G'(x)}{G(x)}\\ F(x) &\equiv \int \frac{G'(x)}{G(x)} \end{aligned} \]

注意常数项为 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 0\pmod {x^n}\)。设我们已经求出 \(\frac{n}{2}\) 的答案 \(F_0(x)\)

\[\begin{aligned} F(x) &\equiv F_0(x)-\frac{H(F_0(x))}{H'(F_0(x))}\\ &\equiv F_0(x)-\frac{\ln(F_0(x)-G(x)}{\frac{1}{F_0(x)}}\\ &\equiv F_0(x)(1-\ln(F_0(x))+G(x)) \end{aligned} \]

可以用 \(O(n\log n)\) 解决。

形式幂级数快速幂

给定幂级数 \(G(x)\),求幂级数 \(F(x)\equiv G^k(x) \pmod{x^n}\)

\[\begin{aligned} \ln(F(x)) &\equiv k\ln(G(x))\\ F(x) &\equiv \exp(k\ln(G(x))) \mod x^n \end{aligned} \]

可以用 \(O(n\log n)\) 解决。

还可以用快速幂解决,常数会更小但是多一个 \(\log\)

形式幂级数开平方

给定幂级数 \(G(x)\),求幂级数 \(F(x)\equiv \sqrt{G(x)} \pmod {x^n}\)。设已经解决了 \(\frac{n}{2}\) 的答案 \(F_0(x)\),然后我们考虑用平方法。

\[F^2(x)-2F(x)F_0(x)+f_0^2(x)\equiv 0\\ G(x)-2F(x)F_0(x)+F_0^2(x)\equiv 0\\ F(x)\equiv \frac{G(x)+F_0^{2}(x)}{2F_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;
	}
}
posted @ 2021-03-08 19:19  LarsWerner  阅读(83)  评论(0编辑  收藏  举报