多项式全家桶

多项式全家桶

#include<bits/stdc++.h>
using namespace std;
const int mod=998244353,maxn=1<<22;
int f[maxn],g[maxn],q[maxn],r[maxn];
inline int add(int x,int y){if ((x+=y)>=mod) x-=mod;return x;}
inline int sub(int x,int y){if ((x-=y)<0) x+=mod;return x;}
int ksm(int x,int y){
	int res=1;
	while (y){
		if (y&1) res=1ll*res*x%mod;
		x=1ll*x*x%mod;
		y>>=1;
	}
	return res;
}
namespace _NTT{
	int rv[maxn];
	void init(int len){
		for (int i=1,t=len>>1;i<=len;++i) rv[i]=(rv[i>>1]>>1)|(i&1?t:0);
	}
	void NTT(int *f,int len,int rev){
		for (int i=0;i<len;++i)
			if (rv[i]<i)
				swap(f[i],f[rv[i]]);
		for (int l=2,m=1;l<=len;l<<=1,m<<=1){
			int step=ksm(3,(mod-1)/l);
			for (int i=0;i<len;i+=l)
				for (int j=0,cur=1;j<m;++j,cur=1ll*cur*step%mod){
					int tmp=1ll*cur*f[i+m+j]%mod;
					f[i+m+j]=sub(f[i+j],tmp);
					f[i+j]=add(f[i+j],tmp);
				}
		}
		if (rev!=-1) return;
		int inv=ksm(len,mod-2);
		for (int i=0;i<len;++i) f[i]=1ll*f[i]*inv%mod;
		reverse(f+1,f+len);
	}
}
using _NTT::init;
using _NTT::NTT;
namespace _getinv{
	int t1[maxn],t2[maxn];
	void getinv(int *f,int len,int *g){
		if (len==1){
			g[0]=ksm(f[0],mod-2);
			return;
		}
		int tlen=(len+1)>>1,klen=1;
		getinv(f,tlen,g);
		while (klen<2*len) klen<<=1;
		for (int i=0;i<tlen;++i) t1[i]=g[i];
		for (int i=tlen;i<klen;++i) t1[i]=0;
		for (int i=0;i<len;++i) t2[i]=f[i];
		for (int i=len;i<klen;++i) t2[i]=0;
		init(klen);
		NTT(t1,klen,1);
		NTT(t2,klen,1);
		for (int i=0;i<klen;++i) t1[i]=sub(add(t1[i],t1[i]),1ll*t1[i]*t1[i]%mod*t2[i]%mod);
		NTT(t1,klen,-1);
		for (int i=0;i<len;++i) g[i]=t1[i];
	}
}
using _getinv::getinv;
namespace _getln{
	int t1[maxn],t2[maxn],inv[maxn];
	void getln(int *f,int len,int *g){
		int klen=1;
		while (klen<2*len) klen<<=1;
		for (int i=0;i<len;++i) t2[i]=f[i];
		getinv(t2,len,t1);
		for (int i=len;i<klen;++i) t1[i]=0;
		for (int i=0;i<len-1;++i) t2[i]=1ll*(i+1)*f[i+1]%mod;
		for (int i=len-1;i<klen;++i) t2[i]=0;
		init(klen);
		NTT(t1,klen,1);
		NTT(t2,klen,1);
		for (int i=0;i<klen;++i) g[i]=1ll*t1[i]*t2[i]%mod;
		NTT(g,klen,-1);
		for (int i=len;i<klen;++i) g[i]=0;
		inv[1]=1;
		for (int i=2;i<len;++i) inv[i]=mod-1ll*(mod/i)*inv[mod%i]%mod;
		for (int i=len-1;i>=1;--i) g[i]=1ll*inv[i]*g[i-1]%mod;
		g[0]=0;
	}
}
using _getln::getln;
namespace _getexp{
	int t1[maxn],t2[maxn];
	void getexp(int *f,int len,int *g){
		if (len==1){
			g[0]=1;
			return;
		}
		int tlen=(len+1)>>1,klen=1;
		while (klen<2*len) klen<<=1;
		getexp(f,tlen,g);
		for (int i=0;i<tlen;++i) t1[i]=g[i];
		for (int i=tlen;i<klen;++i) t1[i]=0;
		getln(t1,len,t2);
		for (int i=0;i<len;++i) t2[i]=sub(f[i],t2[i]);
		t2[0]=add(t2[0],1);
		for (int i=len;i<klen;++i) t2[i]=0;
		for (int i=0;i<tlen;++i) t1[i]=g[i];
		for (int i=tlen;i<klen;++i) t1[i]=0;
		init(klen);
		NTT(t1,klen,1);
		NTT(t2,klen,1);
		for (int i=0;i<klen;++i) t1[i]=1ll*t1[i]*t2[i]%mod;
		NTT(t1,klen,-1);
		for (int i=0;i<len;++i) g[i]=t1[i];
	}
}
using _getexp::getexp;
namespace _getsqrt{
	const int inv2=ksm(2,mod-2);
	int t1[maxn],t2[maxn];
	void getsqrt(int *f,int len,int *g){
		if (len==1){
			g[0]=1;
			return;
		}
		int tlen=(len+1)>>1,klen=1;
		while (klen<2*len) klen<<=1;
		getsqrt(f,tlen,g);
		for (int i=0;i<tlen;++i) t1[i]=add(g[i],g[i]);
		for (int i=tlen;i<klen;++i) t1[i]=0;
		getinv(t1,len,t2);
		for (int i=0;i<len;++i) t1[i]=f[i];
		for (int i=len;i<klen;++i) t1[i]=0;
		init(klen);
		NTT(t1,klen,1);
		NTT(t2,klen,1);
		for (int i=0;i<klen;++i) t1[i]=1ll*t1[i]*t2[i]%mod;
		NTT(t1,klen,-1);
		for (int i=0;i<len;++i) g[i]=add(1ll*inv2*g[i]%mod,t1[i]);
	}
}
using _getsqrt::getsqrt;
namespace _getksm{
	int t1[maxn],t2[maxn];
	char s[maxn];
	void getksm(int *f,int len,int k,int *g){
		if (!k){
			g[0]=1;
			return;
		}
		int A,I,i=0;
		while (!f[i] && i<len) ++i;
		if (i>len) return;
		A=f[i],I=i;
		int inv=ksm(A,mod-2),mi=ksm(A,k);
		for (int i=I;i<len;++i) t2[i-I]=1ll*f[i]*inv%mod;
		getln(t2,len-I,t1);
		for (int i=0;i<len;++i) t1[i]=1ll*t1[i]*k%mod;
		getexp(t1,len-I,t2);
		for (int i=0;i<len-I;++i)
			if (i+I*k<len) g[i+I*k]=1ll*mi*t2[i]%mod;
	}
	void read(int &k){
		scanf("%s",s+1);k=0;
		for (int i=1,len=strlen(s+1);i<=len;++i) k=(10ll*k%mod+s[i]-'0')%mod;
	}
}
using _getksm::getksm;
namespace _getmod{
	int t[maxn],t1[maxn],gr[maxn],fr[maxn];
	void getmod(int *f,int n,int *g,int m,int *q,int *r){
		int len=n-m+1,klen=1;
		while (klen<2*len) klen<<=1;
		for (int i=0;i<klen;++i) fr[i]=gr[i]=t1[i]=0;
		for (int i=0;i<n;++i) fr[i]=f[n-i-1];
		for (int i=0;i<m;++i) gr[i]=g[m-i-1];
		getinv(gr,len,t);
		for (int i=len;i<klen;++i) fr[i]=0;
		init(klen);
		NTT(fr,klen,1);
		NTT(t,klen,1);
		for (int i=0;i<klen;++i) fr[i]=1ll*fr[i]*t[i]%mod;
		NTT(fr,klen,-1);
		for (int i=0;i<len;++i) q[len-i-1]=fr[i];
		for (int i=0;i<len;++i) t1[i]=q[i];
		for (int i=0;i<m;++i) t[i]=g[i];
		for (int i=m;i<klen;++i) t[i]=0;
		klen=1;
		while (klen<2*n) klen<<=1;
		init(klen);
		NTT(t,klen,1);
		NTT(t1,klen,1);
		for (int i=0;i<klen;++i) t[i]=1ll*t[i]*t1[i]%mod;
		NTT(t,klen,-1);
		for (int i=0;i<n;++i) r[i]=sub(f[i],1ll*t[i]);
	}
}
using _getmod::getmod;
int n,m;
int main(){
	freopen("P4512_4.in","r",stdin);
	scanf("%d%d",&n,&m);++n;++m;
	for (int i=0;i<n;++i) scanf("%d",&f[i]);
	for (int i=0;i<m;++i) scanf("%d",&g[i]);
	getmod(f,n,g,m,q,r);
	for (int i=0;i<n-m+1;++i) printf("%d ",q[i]);
	puts("");
	for (int i=0;i<m-1;++i) printf("%d ",r[i]);
	return 0;
}

FFT和NTT

首先最基本的FFT和NTT什么的应该能够默写了吧。

牛顿迭代法

假如有几个关于 A ( x ) A(x) A(x)的函数 g g g,现在要求解方程 g ( A ( x ) ) ≡ 0 m o d    x n g(A(x))\equiv 0 \mod x^n g(A(x))0modxn,那么不妨先求出方程 g ( A 0 ( x ) ) ≡ 0 m o d    x ⌈ n 2 ⌉ g(A_0(x))\equiv 0\mod x^{\lceil \frac{n}{2}\rceil} g(A0(x))0modx2n的解 A 0 ( x ) A_0(x) A0(x),那么 A ( x ) ≡ A 0 ( x ) − g ( A 0 ( x ) ) g ′ ( A 0 ( x ) ) m o d    x n A(x)\equiv A_0(x)-\frac{g(A_0(x))}{g'(A_0(x))} \mod x^n A(x)A0(x)g(A0(x))g(A0(x))modxn。证明的话可以考虑在 A 0 ( x ) A_0(x) A0(x)处泰勒展开,其实已经忘了,但是似乎也不需要知道。

手握这个结论,可以很轻松的推式子。

多项式求逆

A ( x ) A(x) A(x) B ( x ) B(x) B(x)使得 B ( x ) A ( x ) ≡ 1 m o d    x n B(x)A(x)\equiv 1 \mod x^n B(x)A(x)1modxn,当然也可以写作 B ( x ) ≡ A ( x ) − 1 m o d    x n B(x)\equiv A(x)^{-1} \mod x^n B(x)A(x)1modxn

那么构造 g ( f ( x ) ) = f ( x ) − 1 − A ( x ) g(f(x))=f(x)^{-1}-A(x) g(f(x))=f(x)1A(x),套牛顿迭代可以得到 f ( x ) ≡ f 0 ( x ) − g ( f 0 ( x ) ) g ′ ( f 0 ( x ) ) f(x)\equiv f_0(x)-\frac{g(f_0(x))}{g'(f_0(x))} f(x)f0(x)g(f0(x))g(f0(x))。注意!这里的 g ′ ( f 0 ( x ) ) g'(f_0(x)) g(f0(x))是关于 f 0 ( x ) f_0(x) f0(x)的导,所以 g ′ ( f 0 ( x ) ) = − f 0 ( x ) − 2 g'(f_0(x))=-f_0(x)^{-2} g(f0(x))=f0(x)2。至于分子直接带入即可,那么整理一下就是 f ( x ) ≡ 2 f 0 ( x ) − f 0 ( x ) 2 A ( x ) m o d    x n f(x)\equiv 2f_0(x)-f_0(x)^2A(x) \mod x^n f(x)2f0(x)f0(x)2A(x)modxn,把上面的 f f f换成 B B B就可以迭代了。

多项式对数

A ( x ) A(x) A(x) B ( x ) ≡ ln ⁡ A ( x ) m o d    x n B(x) \equiv \ln A(x) \mod x^n B(x)lnA(x)modxn,可以试一下牛顿迭代,可以发现构造的函数里面有 exp ⁡ B ( x ) \exp B(x) expB(x),行不通,所以这里采取求导再积分的方式。
d ln ⁡ A ( x ) d x ≡ f ′ ( x ) f ( x ) m o d    x n ln ⁡ f ( x ) ≡ ∫ d ln ⁡ x ≡ ∫ f ′ ( x ) f ( x ) d x m o d    x n \begin{aligned} \frac{\mathrm{d} \ln{A(x)}}{\mathrm{dx}}&\equiv \frac{f'(x)}{f(x)}& \mod x^n\\ \ln{f(x)}&\equiv \int \mathrm{d} \ln{x}\equiv \int \frac{f'(x)}{f(x)} \mathrm{dx}& \mod x^n \end{aligned} dxdlnA(x)lnf(x)f(x)f(x)dlnxf(x)f(x)dxmodxnmodxn
那么求导就是简单的位移,求逆上面有了,然后卷一次,最后积分回去即可。

注意,这个要求 [ x 0 ] A ( x ) = 1 [x^0]A(x) =1 [x0]A(x)=1

多项式指数

A ( x ) A(x) A(x) B ( x ) ≡ exp ⁡ A ( x ) m o d    x n B(x) \equiv \exp {A(x)} \mod x^n B(x)expA(x)modxn,这个可以牛顿迭代,直接构造 g ( f ( x ) ) = ln ⁡ f ( x ) − A ( x ) g(f(x))=\ln f(x)-A(x) g(f(x))=lnf(x)A(x),那么 f ( x ) = f 0 ( x ) ( 1 − ln ⁡ f 0 ( x ) + A ( x ) ) m o d    x n f(x)=f_0(x)(1-\ln f_0(x)+A(x)) \mod x^n f(x)=f0(x)(1lnf0(x)+A(x))modxn,里面的那个 ln ⁡ \ln ln直接用上面的就好了。

注意,这里要求 [ x 0 ] A ( x ) = 0 [x^0]A(x)=0 [x0]A(x)=0

多项式开根

A ( x ) A(x) A(x) B ( x ) B(x) B(x)使得 B ( x ) 2 ≡ A ( x ) m o d    x n B(x)^2\equiv A(x) \mod x^n B(x)2A(x)modxn,还是牛顿迭代,可以得到 f ( x ) ≡ f 0 ( x ) 2 + A ( x ) 2 f 0 ( x ) m o d    x n f(x)\equiv \frac{f_0(x)^2+A(x)}{2f_0(x)} \mod x^n f(x)2f0(x)f0(x)2+A(x)modxn

多项式快速幂

A ( x ) A(x) A(x) B ( x ) ≡ A ( x ) k m o d    x n B(x)\equiv A(x)^k \mod x^n B(x)A(x)kmodxn,那么可以简单的推一下式子就是 B ( x ) ≡ A ( x ) k ≡ exp ⁡ ln ⁡ A ( x ) k ≡ exp ⁡ k ln ⁡ A ( x ) m o d    x n B(x)\equiv A(x)^k \equiv \exp \ln A(x)^k \equiv \exp k\ln A(x) \mod x^n B(x)A(x)kexplnA(x)kexpklnA(x)modxn,于是就可以求解了。

当然由于 ln ⁡ \ln ln exp ⁡ \exp exp [ x 0 ] A ( x ) [x^0]A(x) [x0]A(x)都是由要求的,所以可能要先位移一下。

多项式取模

A ( x ) , B ( x ) A(x),B(x) A(x),B(x) Q ( x ) , R ( x ) Q(x),R(x) Q(x),R(x),其中 A ( x ) ≡ Q ( x ) B ( x ) + R ( x ) m o d    n , deg ⁡ Q ( x ) = deg ⁡ A ( x ) − deg ⁡ B ( x ) , deg ⁡ R ( x ) ≤ deg ⁡ B ( x ) A(x)\equiv Q(x)B(x)+R(x) \mod n,\deg Q(x)=\deg A(x)-\deg B(x),\deg R(x)\le \deg B(x) A(x)Q(x)B(x)+R(x)modn,degQ(x)=degA(x)degB(x),degR(x)degB(x)

这个比较特别,思路不一样。

首先定义 f R ( x ) ≡ x n f ( 1 x ) m o d    x n f_R(x)\equiv x^nf(\frac{1}{x}) \mod x^n fR(x)xnf(x1)modxn,就是保留 n n n项并反转。

那么开始推式子:
A ( x ) ≡ Q ( x ) B ( x ) + R ( x ) m o d    x n A ( 1 x ) ≡ Q ( 1 x ) B ( 1 x ) + R ( 1 x ) m o d    x n x n A ( 1 x ) ≡ x n − m Q ( 1 x ) x m B ( 1 x ) + x n − m x m R ( 1 x ) m o d    x n A R ( x ) ≡ Q R ( x ) B R ( x ) + x n − m R R ( x ) m o d    x n A R ( x ) ≡ Q R ( x ) B R ( x ) m o d    x n − m \begin{aligned} A(x) &\equiv Q(x)B(x)+R(x) &\mod x^n\\ A(\frac{1}{x}) & \equiv Q(\frac{1}{x})B(\frac{1}{x})+R(\frac{1}{x}) &\mod x^n\\ x^nA(\frac{1}{x}) & \equiv x^{n-m}Q(\frac{1}{x})x^mB(\frac{1}{x})+x^{n-m}x^{m}R(\frac{1}{x}) & \mod x^n\\ A_R(x) & \equiv Q_R(x)B_R(x)+x^{n-m}R_R(x) & \mod x^n\\ A_R(x) & \equiv Q_R(x)B_R(x) &\mod x^{n-m}\\ \end{aligned} A(x)A(x1)xnA(x1)AR(x)AR(x)Q(x)B(x)+R(x)Q(x1)B(x1)+R(x1)xnmQ(x1)xmB(x1)+xnmxmR(x1)QR(x)BR(x)+xnmRR(x)QR(x)BR(x)modxnmodxnmodxnmodxnmodxnm
上面的式子可以直接 m o d    x n − m \mod x^{n-m} modxnm是因为 deg ⁡ Q ( x ) = n − m \deg Q(x)=n-m degQ(x)=nm,这样 Q ( x ) Q(x) Q(x)就求出来了,然后带入可以求出 R ( x ) R(x) R(x)

……

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值