多项式全家桶

发布时间 2023-03-27 20:11:21作者: nich666

前置知识:

\(FFT,NTT\)

但主要是\(NTT\),常数小,不会损失精度,在模意义下更常用

我太懒,不想讲了

注意事项:

1.很多函数要清空,清空,清空
2.两个多项式在模意义下相乘时,要将高位赋值为\(0\)
3.开\(long\ long\)
4.要封装到一个\(namespace\)中,不然以后会很痛苦
5.我的代码常数很大,且用了#define int long long邪教,请勿模仿,不然很可能被卡常
6.下标默认从\(0\)开始

FFT/NTT

这个可以去看cmd的博客,这里就只贴个板子

namespace Math
{
	const int mod=998244353;
	inline int qpow(int a,int b=mod-2,int ans=1)
	{
		for(;b;b>>=1,(a*=a)%=mod) if(b&1) (ans*=a)%=mod;
		return ans;
	}
	inline int inv(int x){return qpow(x);}
}
namespace Poly
{
	const int mod=998244353;
	int rev[N];
    const double PI=acos(-1);
	struct node
	{
		double x,y;
		inline node(double xx=0,double yy=0){x=xx,y=yy;}
		inline node operator + (node a){return node(x+a.x,y+a.y);}
		inline node operator - (node a){return node(x-a.x,y-a.y);}
		inline node operator * (node a){return node(x*a.x-y*a.y,x*a.y+y*a.x);}
	}a[N],b[N];
	int rev[N];
	void fft(node *a,int tot,int tag)
	{
		for(int i=0;i<tot;++i) if(i<rev[i]) swap(a[i],a[rev[i]]);
		for(int mid=1;mid<tot;mid<<=1)
		{
			node w=node(cos(PI/mid),tag*sin(PI/mid)),g=node(1,0),x,y;
			for(int i=0;i<tot;i+=mid<<1,g=node(1,0))
				for(int j=0;j<mid;++j,g=g*w)
					x=a[i+j],y=g*a[i+j+mid],
					a[i+j]=x+y,a[i+j+mid]=x-y;
		}
	}
	inline void FFT(int *A,int *B,int n,int m)
	{
		for(int i=0;i<=n;++i) a[i].x=A[i];
		for(int i=0;i<=m;++i) b[i].x=B[i];
		int bit=0,tot=1;
		while((1<<bit)<n+m+1) ++bit;tot=1<<bit;
		for(int i=0;i<tot;++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1));
		fft(a,tot,1),fft(b,tot,1);
		for(int i=0;i<tot;++i) a[i]=a[i]*b[i];
		fft(a,tot,-1);
		for(int i=0;i<=n+m;++i) A[i]=(a[i].x/tot+0.5);
	}
	const int G=3,invG=Math::inv(G);
	inline void ntt(int *A,int tot,int tag)
	{
		for(int i=0;i<tot;++i) if(i<rev[i]) swap(A[i],A[rev[i]]);
		for(int len=1;len<tot;len<<=1)
		{
			int Gn=Math::qpow(tag?G:invG,(mod-1)/len/2),x,y;
			for(int i=0;i<tot;i+=len*2)
			{
			 	int buf=1;
				for(int j=0;j<len;++j,(buf*=Gn)%=mod)
					x=A[i+j],y=buf*A[i+j+len]%mod,
					A[i+j]=(x+y)%mod,A[i+j+len]=(x-y+mod)%mod;
			}	
		}
	}
	inline void NTT(int *A,int *B,int n,int m)
	{
		int bit=0,tot=0;
		while((1<<bit)<n+m+1) ++bit;tot=1<<bit;
		for(int i=0;i<tot;++i) rev[i]=(rev[i>>1]>>1)|((i&1)?tot>>1:0);
		ntt(A,tot,1),ntt(B,tot,1);
		for(int i=0;i<tot;++i) A[i]=A[i]*B[i]%mod;
		ntt(A,tot,0);int invtot=Math::inv(tot);
		for(int i=0;i<tot;++i)A[i]=(A[i]*invtot)%mod;
	}
}

拉格朗日插值

记住一个式子

\[Ans=\sum_{i=1}^ny_i\prod_{i\neq j}\frac{x-x_j}{x_i-x_j} \]

这个式子十分巧妙,我们发现\(y_i\prod_{i\neq j}\frac{x-x_j}{x_i-x_j}\)只在\(i\)的时候贡献\(y_i\),对其他\(n-1\)则贡献为\(0\),然后就结束了

namespace Poly
{
	const int mod=998244353;
	inline int langrange(int *A,int *B,int n,int k)
	{
		int ans=0;
		for(int i=1;i<=n;++i)
		{
			int s1=1,s2=1;
			for(int j=1;j<=n;++j)
				if(i!=j) (s1*=k-A[j])%=mod,(s2*=A[i]-A[j])%=mod;
			(ans+=B[i]*s1%mod*Math::inv(s2)%mod)%=mod;
		}
		return (ans+mod)%mod;
	}
}

多项式牛顿迭代

给出函数\(g(x)\),求出一个\(f(x)\),使

\[g(f(x))\equiv 0(\bmod x^n) \]

我们递归考虑:

若我们已经知道\(t(x)\),使:

\[g(t(x))\equiv 1(\bmod x^\left \lceil\frac{n}{2} \right \rceil ) \]

则:

\[f(x)-t(x)\equiv 0(\bmod x^{\left\lceil\frac{n}{2}\right\rceil}) \]

因为泰勒展开:

\[f(x)=\sum_{n=0}^\infty\frac{f^{(n)}(x_0)}{n!}(x-x_0)^n \]

\[f(x)=f(x_0)+f'(x_0)(x-x_0)+\sum_{n=2}^\infty\frac{f^{(n)}(x_0)}{n!}(x-x_0)^n \]

\(g(x),f(x),t(x)\)代入\(f(x),x,x_0\),得到

\[g(f(x))=g(t(x))+g'(t(x))(g(x)-t(x))+\sum_{n=2}^\infty\frac{g^{(n)}(t(x))}{n!}(f(x)-g(x))^n \]

因为:

\[\forall n\ge2,(f(x)-t(x))^n\equiv 0(\bmod x^n) \]

所以

\[g(f(x))\equiv g(t(x))+g'(t(x))(g(x)-t(x)) \]

整理得到:

\[f(x)\equiv t(x)-\frac{g(t(x))}{g'(t(x))} \]

这个有什么用呢,等会可以拿来推导式子

多项式求逆

板子链接

给出一个\(n\)次多项式\(A(x)\),求出一个\(n\)次多项式\(B(x)\),使

\[A(x)\cdot B(x)\equiv1(\bmod x^n) \]

\(g(x)=\frac{1}{x}-A(x)\),则\(g(B(x))\equiv 0\)

套路地令

\[t(x)为A(x)\cdot t(x)\equiv1(\bmod x^{\left\lceil\frac{n}{2}\right\rceil})的解 \]

\[B(x)=t(x)-\frac{\frac{1}{t(x)}-A(x)}{(\frac{1}{t(x)}-A(x))'}=t(x)-\frac{\frac{1}{t(x)}-A(x)}{-\frac{1}{t^2(x)}}\\=(2-A(x)t(x))t(x) \]

然后递推+\(NTT\)求解就行了

namespace Poly
{
	const int mod=998244353;
	int rev[N];
	const int G=3,invG=Math::inv(G);
	inline int md(int x){return x>=mod?x-mod:x<0?x+mod:x;}
	inline void ntt(int *A,int tot,int tag)
	{
		for(int i=0;i<tot;++i) if(i<rev[i]) swap(A[i],A[rev[i]]);
		for(int len=1;len<tot;len<<=1)
		{
			int Gn=Math::qpow(tag?G:invG,(mod-1)/len/2),x,y;
			for(int i=0;i<tot;i+=len*2)
			{
			 	int buf=1;
				for(int j=0;j<len;++j,(buf*=Gn)%=mod)
					x=A[i+j],y=buf*A[i+j+len]%mod,
					A[i+j]=md(x+y),A[i+j+len]=md(x-y);
			}	
		}
	}
	inline void NTT(int *A,int *B,int n,int m)
	{
		int bit=0,tot=0;
		while((1<<bit)<n+m+1) ++bit;tot=1<<bit;
		for(int i=0;i<tot;++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1));
		ntt(A,tot,1),ntt(B,tot,1);
		for(int i=0;i<tot;++i) A[i]=A[i]*B[i]%mod;
		ntt(A,tot,0);int invtot=Math::inv(tot);
		for(int i=0;i<=n+m;++i)(A[i]=A[i]*invtot)%=mod;
	}
	int A[N],B[N];
	inline void niyuan(int *a,int *b,int n)
	{
		memset(A,0,n*16),memset(B,0,n*16);
		b[0]=Math::inv(a[0]);
		for(int l=2,m=1;l<=n*4;m<<=1,l<<=1)
		{
			for(int i=0;i<m;++i) A[i]=a[i],B[i]=b[i];
			for(int i=0;i<l;++i) rev[i]=(rev[i>>1]>>1)|((i&1)?m:0);
			ntt(A,l,1),ntt(B,l,1);
			for(int i=0;i<l;++i) b[i]=((2-A[i]*B[i]%mod)*B[i]%mod+mod)%mod;
			ntt(b,l,0);int inv=Math::inv(l);
			for(int i=0;i<l;++i) (b[i]*=inv)%=mod;
			for(int i=m;i<l;++i) b[i]=0;
		}
	}
}

多项式开根

板子题链接

给定一个 \(n-1\) 次多项式 \(A(x)\),求一个在 \({} \bmod x^n\) 意义下的多项式 \(B(x)\),使得 \(B^2(x) \equiv A(x) \pmod{x^n}\)。若有多解,请取零次项系数较小的作为答案。

多项式的系数在 \({}\bmod 998244353\) 的意义下进行运算。

\(g(x)=x^2-A(x)\),\(t(x)\)的定义与上面差不多,则

\[B(x)=t(x)-\frac{t^2(x)-A(x)}{2t(x)}=\frac{1}{2}(t(x)+\frac{A(x)}{t(x)}) \]

写多项式求逆就行了

namespace Poly
{
	const int mod=998244353;
	int rev[N];
	const int G=3,invG=Math::inv(G);
	inline int md(int x){return x>=mod?x-mod:x<0?x+mod:x;}
	inline void ntt(int *A,int tot,int tag)
	{
		for(int i=0;i<tot;++i) if(i<rev[i]) swap(A[i],A[rev[i]]);
		for(int len=1;len<tot;len<<=1)
		{
			int Gn=Math::qpow(tag?G:invG,(mod-1)/len/2),x,y;
			for(int i=0;i<tot;i+=len*2)
			{
			 	int buf=1;
				for(int j=0;j<len;++j,(buf*=Gn)%=mod)
					x=A[i+j],y=buf*A[i+j+len]%mod,
					A[i+j]=md(x+y),A[i+j+len]=md(x-y);
			}	
		}
	}
	inline void NTT(int *A,int *B,int n,int m)
	{
		int bit=0,tot=0;
		while((1<<bit)<n+m+1) ++bit;tot=1<<bit;
		for(int i=0;i<tot;++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1));
		ntt(A,tot,1),ntt(B,tot,1);
		for(int i=0;i<tot;++i) A[i]=A[i]*B[i]%mod;
		ntt(A,tot,0);int invtot=Math::inv(tot);
		for(int i=0;i<=n+m;++i)(A[i]=A[i]*invtot)%=mod;
	}
	int A[N],B[N];
	inline void niyuan(int *a,int *b,int n)
	{
		memset(A,0,n*16),memset(B,0,n*16);
		b[0]=Math::inv(a[0]);
		for(int l=2,m=1;l<n*4;m<<=1,l<<=1)
		{
			for(int i=0;i<m;++i) A[i]=a[i],B[i]=b[i];
			for(int i=0;i<l;++i) rev[i]=(rev[i>>1]>>1)|((i&1)?m:0);
			ntt(A,l,1),ntt(B,l,1);
			for(int i=0;i<l;++i) b[i]=((2-A[i]*B[i]%mod)*B[i]%mod+mod)%mod;
			ntt(b,l,0);int inv=Math::inv(l);
			for(int i=0;i<l;++i) (b[i]*=inv)%=mod;
			for(int i=m;i<l;++i) b[i]=0;
		}
	}
	int Inv2=Math::inv(2),C[N],Inv[N];
	inline void kaigen(int *a,int *b,int n)
	{
		memset(C,0,n*16),memset(Inv,0,n*16);
		b[0]=1;
		for(int l=2,m=1;l<n*4;l<<=1,m<<=1)
		{
			for(int i=0;i<m;++i) C[i]=a[i];
			niyuan(b,Inv,m);
			for(int i=0;i<l;++i) rev[i]=(rev[i>>1]>>1)|((i&1)?m:0);
			ntt(C,l,1),ntt(Inv,l,1);
			for(int i=0;i<l;++i) C[i]=C[i]*Inv[i]%mod;
			ntt(C,l,0);int inv=Math::inv(l);
			for(int i=0;i<l;++i) C[i]=C[i]*inv%mod;
			for(int i=1;i<m;++i) b[i]=Inv2*(b[i]+C[i])%mod;
		}
	}
}

多项式求导和积分

这个就跟数学上的差不多,可以自己学一下(一般积分时忽略常数\(C\))

\[(\sum_{n=0}^\infty a_nx^n)'=\sum_{n=1}^\infty na_nx^{n-1}\\ \int \sum_{n=0}^\infty a_nx^n=\sum_{n=1}^\infty\frac{a_{n-1}}{n}x^n \]

代码:

namespace Poly
{
    inline void qiudao(int *A,int *B,int n)
	{
		for(int i=1;i<n;++i) B[i-1]=i*A[i]%mod;
		return B[n-1]=0,void();
	}
	inline void jifen(int *A,int *B,int n)
	{
		for(int i=1;i<n;++i) B[i]=A[i-1]*Math::inv(i)%mod;
		return B[0]=0,void();
	}
}

多项式对数函数(多项式ln)

板子题链接

给出 \(n-1\) 次多项式 \(A(x)\),求一个 \(\bmod{\:x^n}\) 下的多项式 \(B(x)\),满足 \(B(x) \equiv \ln A(x)\).

\(\text{mod } 998244353\) 下进行,且 \(a_i \in [0, 998244353) \cap \mathbb{Z}\)

我们手推一下式子:

\[B(x)=\ln A(x)\\ 对左右两边求导\\ B'(x)=\frac{A'(x)}{A(x)}\\ B(x)=\int \frac{A'(x)}{A(x)} \]

然后只用写求导,积分和求逆三个板子就行了

分治FFT

链接

给定序列 \(g_{1\dots n - 1}\),求序列 \(f_{0\dots n - 1}\)

其中 \(f_i=\sum_{j=1}^if_{i-j}g_j\),边界为 \(f_0=1\)

答案对 \(998244353\) 取模。

就跟\(CDQ\)分治一样

具体实现看代码:

namespace Poly
{
	const int mod=998244353;
	int rev[N];
	const int G=3,invG=Math::inv(G);
	inline void ntt(int *A,int tot,int tag)
	{
		for(int i=0;i<tot;++i) if(i<rev[i]) swap(A[i],A[rev[i]]);
		for(int len=1;len<tot;len<<=1)
		{
			int Gn=Math::qpow(tag?G:invG,(mod-1)/len/2),x,y;
			for(int i=0;i<tot;i+=len*2)
			{
			 	int buf=1;
				for(int j=0;j<len;++j,(buf*=Gn)%=mod)
					x=A[i+j],y=buf*A[i+j+len]%mod,
					A[i+j]=(x+y)%mod,A[i+j+len]=(x-y+mod)%mod;
			}	
		}
	}
	inline void NTT(int *A,int *B,int n,int m)
	{
		int bit=0,tot=0;
		while((1<<bit)<n+m+1) ++bit;tot=1<<bit;
		for(int i=0;i<tot;++i) rev[i]=(rev[i>>1]>>1)|((i&1)?tot>>1:0);
		ntt(A,tot,1),ntt(B,tot,1);
		for(int i=0;i<tot;++i) A[i]=A[i]*B[i]%mod;
		ntt(A,tot,0);int invtot=Math::inv(tot);
		for(int i=0;i<tot;++i)A[i]=(A[i]*invtot)%mod;
	}
	int A[N],B[N];
	void fenzhi(int *a,int *b,int l,int r)
	{
		if(l==r) return;
		int mid=(l+r)>>1,len=r-l+1;
		fenzhi(a,b,l,mid);
		for(int i=0;i<=4*len;++i) A[i]=B[i]=0;
		for(int i=0;i<=mid-l;++i) A[i]=b[i+l];
		for(int i=0;i<len;++i) B[i]=a[i+1];
		NTT(A,B,mid-l+1,len);
		for(int i=mid+1;i<=r;++i) b[i]=(b[i]+A[i-l-1])%mod;
		fenzhi(a,b,mid+1,r);
	}
}

多项式指数函数(多项式exp)

链接

给出 \(n-1\) 次多项式 \(A(x)\),求一个 \(\bmod{\:x^n}\) 下的多项式 \(B(x)\),满足 \(B(x) \equiv \text e^{A(x)}\)。系数对 \(998244353\) 取模。

也是推式子,令\(g(x)=\ln x-A(x)\),\(t(x)\)的定义和上面相似,则

\[B(x)=t(x)-\frac{\ln t(x)-A(x)}{\frac{1}{t(x)}}\\=t(x)(1-\ln t(x)+A(x)) \]

写多项式\(\ln\)就行了

常数巨大的\(O(n\log n)\)

也有\(O(n\log^2 n)\)的分治\(NTT\)写法

\[B(x)\equiv e^{A(x)}\\B'(x)\equiv e^{A(x)}A'(x)\\B'(x)\equiv B(x)A'(x)\\ B(x)=\int B(x)A'(x) \]

然后分治NTT就行了(据说跑的比\(O(n\log n)\)快)

多项式快速幂

链接

给定一个 \(n-1\) 次多项式 \(A(x)\),求一个在 \(\bmod\ x^n\) 意义下的多项式 \(B(x)\),使得 \(B(x) \equiv (A(x))^k \ (\bmod\ x^n)\)

多项式的系数在 \(\bmod\ 998244353\) 的意义下进行运算。

推式子:

\[B(x)\equiv A(x)^k(\bmod x^n)\\ \ln B(x)\equiv k\ln A(x)(\bmod x^n)\\ B(x)\equiv e^{k\ln A(x)} \]

写个多项式\(\ln\)和多项式\(exp\)就行了

namespace Poly
{
	const int mod=998244353;
	int rev[N];
	const int G=3,invG=Math::inv(G);
	inline void ntt(int *A,int tot,int tag)
	{
		for(int i=0;i<tot;++i) if(i<rev[i]) swap(A[i],A[rev[i]]);
		for(int len=1;len<tot;len<<=1)
		{
			int Gn=Math::qpow(tag?G:invG,(mod-1)/len/2),x,y;
			for(int i=0;i<tot;i+=len*2)
			{
			 	int buf=1;
				for(int j=0;j<len;++j,(buf*=Gn)%=mod)
					x=A[i+j],y=buf*A[i+j+len]%mod,
					A[i+j]=(x+y)%mod,A[i+j+len]=(x-y+mod)%mod;
			}	
		}
	}
	inline void NTT(int *A,int *B,int n,int m)
	{
		int bit=0,tot=0;
		while((1<<bit)<n+m+1) ++bit;tot=1<<bit;
		for(int i=0;i<tot;++i) rev[i]=(rev[i>>1]>>1)|((i&1)?tot>>1:0);
		ntt(A,tot,1),ntt(B,tot,1);
		for(int i=0;i<tot;++i) A[i]=A[i]*B[i]%mod;
		ntt(A,tot,0);int invtot=Math::inv(tot);
		for(int i=0;i<tot;++i)A[i]=(A[i]*invtot)%mod;
	}
	inline void qiudao(int *A,int *B,int n)
	{
		for(int i=0;i<4*n;++i) B[i]=0;
		for(int i=1;i<n;++i) B[i-1]=i*A[i]%mod;
		return B[n-1]=0,void();
	}
	inline void jifen(int *A,int *B,int n)
	{
		for(int i=0;i<4*n;++i) B[i]=0;
		for(int i=1;i<n;++i) B[i]=A[i-1]*Math::inv(i)%mod;
		return B[0]=0,void();
	}
	int A[N],B[N];
	inline void qiuni(int *a,int *b,int n)
	{
		for(int i=0;i<4*n;++i) A[i]=B[i]=b[i]=0;
		b[0]=Math::inv(a[0]);
		for(int l=2,m=1;l<n*4;m<<=1,l<<=1)
		{
			for(int i=0;i<m;++i) A[i]=a[i],B[i]=b[i];
			for(int i=0;i<l;++i) rev[i]=(rev[i>>1]>>1)|((i&1)?m:0);
			ntt(A,l,1),ntt(B,l,1);
			for(int i=0;i<l;++i) b[i]=((2-A[i]*B[i]%mod)*B[i]%mod+mod)%mod;
			ntt(b,l,0);int inv=Math::inv(l);
			for(int i=0;i<l;++i) (b[i]*=inv)%=mod;
			for(int i=m;i<l;++i) b[i]=0;
		}
	}
	int C[N],Inv[N];
	inline void ln(int *a,int *b,int n)
	{
		for(int i=0;i<4*n;++i) b[i]=C[i]=Inv[i]=0;
		qiudao(a,C,n),qiuni(a,Inv,n);
		NTT(C,Inv,n,n);
		jifen(C,b,n);
	}
	int D[N],E[N];
	inline void exp(int *a,int *b,int n)
	{
		for(int i=0;i<4*n;++i) b[i]=D[i]=E[i]=0;
		b[0]=1;
		for(int l=4,m=2;l<=n*4;l<<=1,m<<=1)
		{
			for(int i=m;i<l;++i) D[i]=E[i]=0;
			for(int i=0;i<m;++i) E[i]=a[i];
			ln(b,D,m);
			for(int i=0;i<m;++i) E[i]=(mod-D[i]+E[i])%mod;
			(E[0]+=1)%=mod;
			NTT(b,E,l,l);
			for(int i=m;i<l;++i) b[i]=0;
		}
	}
	int H[N];
	inline void pow(int *a,int *b,int n,int k)
	{
		ln(a,H,n);
		for(int i=0;i<n;++i) (H[i]*=k)%=mod;
		exp(H,b,n);
	}
}

目前的全家桶

namespace Math
{
	const int mod=998244353;
	inline int qpow(int a,int b=mod-2,int ans=1)
	{
		for(;b;b>>=1,(a*=a)%=mod) if(b&1) (ans*=a)%=mod;
		return ans;
	}
	inline int inv(int x){return qpow(x);}
}
namespace Poly
{
	const int mod=998244353;
	int rev[N];
	inline int langrange(int *A,int *B,int n,int k)
	{
		int ans=0;
		for(int i=1;i<=n;++i)
		{
			int s1=1,s2=1;
			for(int j=1;j<=n;++j)
				if(i!=j) (s1*=k-A[j])%=mod,(s2*=A[i]-A[j])%=mod;
			(ans+=B[i]*s1%mod*Math::inv(s2)%mod)%=mod;
		}
		return (ans+mod)%mod;
	}
	const double PI=acos(-1);
	struct node
	{
		double x,y;
		inline node(double xx=0,double yy=0){x=xx,y=yy;}
		inline node operator + (node a){return node(x+a.x,y+a.y);}
		inline node operator - (node a){return node(x-a.x,y-a.y);}
		inline node operator * (node a){return node(x*a.x-y*a.y,x*a.y+y*a.x);}
	}a[N],b[N];
	int rev[N];
	void fft(node *a,int tot,int tag)
	{
		for(int i=0;i<tot;++i) if(i<rev[i]) swap(a[i],a[rev[i]]);
		for(int mid=1;mid<tot;mid<<=1)
		{
			node w=node(cos(PI/mid),tag*sin(PI/mid)),g=node(1,0),x,y;
			for(int i=0;i<tot;i+=mid<<1,g=node(1,0))
				for(int j=0;j<mid;++j,g=g*w)
					x=a[i+j],y=g*a[i+j+mid],
					a[i+j]=x+y,a[i+j+mid]=x-y;
		}
	}
	inline void FFT(int *A,int *B,int n,int m)
	{
		for(int i=0;i<=n;++i) a[i].x=A[i];
		for(int i=0;i<=m;++i) b[i].x=B[i];
		int bit=0,tot=1;
		while((1<<bit)<n+m+1) ++bit;tot=1<<bit;
		for(int i=0;i<tot;++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1));
		fft(a,tot,1),fft(b,tot,1);
		for(int i=0;i<tot;++i) a[i]=a[i]*b[i];
		fft(a,tot,-1);
		for(int i=0;i<=n+m;++i) A[i]=(a[i].x/tot+0.5);
	}
	const int G=3,invG=Math::inv(G);
	inline void ntt(int *A,int tot,int tag)
	{
		for(int i=0;i<tot;++i) if(i<rev[i]) swap(A[i],A[rev[i]]);
		for(int len=1;len<tot;len<<=1)
		{
			int Gn=Math::qpow(tag?G:invG,(mod-1)/len/2),x,y;
			for(int i=0;i<tot;i+=len*2)
			{
			 	int buf=1;
				for(int j=0;j<len;++j,(buf*=Gn)%=mod)
					x=A[i+j],y=buf*A[i+j+len]%mod,
					A[i+j]=(x+y)%mod,A[i+j+len]=(x-y+mod)%mod;
			}	
		}
	}
	inline void NTT(int *A,int *B,int n,int m)
	{
		int bit=0,tot=0;
		while((1<<bit)<n+m+1) ++bit;tot=1<<bit;
		for(int i=0;i<tot;++i) rev[i]=(rev[i>>1]>>1)|((i&1)?tot>>1:0);
		ntt(A,tot,1),ntt(B,tot,1);
		for(int i=0;i<tot;++i) A[i]=A[i]*B[i]%mod;
		ntt(A,tot,0);int invtot=Math::inv(tot);
		for(int i=0;i<tot;++i)A[i]=(A[i]*invtot)%mod;
	}
	inline void qiudao(int *A,int *B,int n)
	{
		for(int i=0;i<4*n;++i) B[i]=0;
		for(int i=1;i<n;++i) B[i-1]=i*A[i]%mod;
		return B[n-1]=0,void();
	}
	inline void jifen(int *A,int *B,int n)
	{
		for(int i=0;i<4*n;++i) B[i]=0;
		for(int i=1;i<n;++i) B[i]=A[i-1]*Math::inv(i)%mod;
		return B[0]=0,void();
	}
	int A[N],B[N];
	inline void qiuni(int *a,int *b,int n)
	{
		for(int i=0;i<4*n;++i) A[i]=B[i]=b[i]=0;
		b[0]=Math::inv(a[0]);
		for(int l=2,m=1;l<n*4;m<<=1,l<<=1)
		{
			for(int i=0;i<m;++i) A[i]=a[i],B[i]=b[i];
			for(int i=0;i<l;++i) rev[i]=(rev[i>>1]>>1)|((i&1)?m:0);
			ntt(A,l,1),ntt(B,l,1);
			for(int i=0;i<l;++i) b[i]=((2-A[i]*B[i]%mod)*B[i]%mod+mod)%mod;
			ntt(b,l,0);int inv=Math::inv(l);
			for(int i=0;i<l;++i) (b[i]*=inv)%=mod;
			for(int i=m;i<l;++i) b[i]=0;
		}
	}
	int Inv2=Math::inv(2),C[N],Inv[N];
	inline void kaigen(int *a,int *b,int n)
	{
		memset(C,0,n*16),memset(Inv,0,n*16);
		b[0]=1;
		for(int l=2,m=1;l<n*4;l<<=1,m<<=1)
		{
			for(int i=0;i<m;++i) C[i]=a[i];
			niyuan(b,Inv,m);
			for(int i=0;i<l;++i) rev[i]=(rev[i>>1]>>1)|((i&1)?m:0);
			ntt(C,l,1),ntt(Inv,l,1);
			for(int i=0;i<l;++i) C[i]=C[i]*Inv[i]%mod;
			ntt(C,l,0);int inv=Math::inv(l);
			for(int i=0;i<l;++i) C[i]=C[i]*inv%mod;
			for(int i=1;i<m;++i) b[i]=Inv2*(b[i]+C[i])%mod;
		}
	inline void ln(int *a,int *b,int n)
	{
		for(int i=0;i<4*n;++i) b[i]=C[i]=Inv[i]=0;
		qiudao(a,C,n),qiuni(a,Inv,n);
		NTT(C,Inv,n,n);
		jifen(C,b,n);
	}
	void fenzhi(int *a,int *b,int l,int r)
	{
		if(l==r) return;
		int mid=(l+r)>>1,len=r-l+1;
		fenzhi(a,b,l,mid);
		for(int i=0;i<=4*len;++i) A[i]=B[i]=0;
		for(int i=0;i<=mid-l;++i) A[i]=b[i+l];
		for(int i=0;i<len;++i) B[i]=a[i+1];
		NTT(A,B,mid-l+1,len);
		for(int i=mid+1;i<=r;++i) b[i]=(b[i]+A[i-l-1])%mod;
		fenzhi(a,b,mid+1,r);
	}
	int D[N],E[N];
	inline void exp(int *a,int *b,int n)
	{
		for(int i=0;i<4*n;++i) b[i]=D[i]=E[i]=0;
		b[0]=1;
		for(int l=4,m=2;l<=n*4;l<<=1,m<<=1)
		{
			for(int i=m;i<l;++i) D[i]=E[i]=0;
			for(int i=0;i<m;++i) E[i]=a[i];
			ln(b,D,m);
			for(int i=0;i<m;++i) E[i]=(mod-D[i]+E[i])%mod;
			(E[0]+=1)%=mod;
			NTT(b,E,l,l);
			for(int i=m;i<l;++i) b[i]=0;
		}
	}
	int H[N];
	inline void pow(int *a,int *b,int n,int k)
	{
		ln(a,H,n);
		for(int i=0;i<n;++i) (H[i]*=k)%=mod;
		exp(H,b,n);
	}
}

留的坑

多项式除法,多项式取模,\(FWT\),多项式三角函数,多项式反三角函数
一车东西的任意模数版
多项式多点求值|快速插值
多项式平移|连续点值平移
常系数齐次线性递推
普通多项式与下降幂多项式互化
在生成函数中的应用
可能会(gu)填(gu)坑(gu)的(gu)