1. 程式人生 > 實用技巧 >多項式全家桶(結論版)

多項式全家桶(結論版)

多項式乘法

略,這個太水了(從另一個方面,其實這是最難的,這取決於你寫不寫證明)。

多項式求逆

\[ B_0(x)\equiv A^{-1}(x) \pmod {x^{\lceil \frac n 2 \rceil}}\\ B(x)\equiv B_0(x)(2-A(x)B_0(x)) \pmod {x^n} \]

多項式\(\ln\)

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

多項式\(\exp\)

\[ B_0(x)\equiv e^{A(x)} \pmod{x^{\lceil \frac n 2 \rceil}}\\ B(x)\equiv B_0(x)(1-\ln B_0(x)+A(x)) \pmod{x^n} \]

事實上,這幾種多項式演算法是包含關係,從最後一個開始,每一個都包含了以前所有的演算法。
程式碼(其實這是從多項式\(\exp\)板子題那兒複製下來的,但它本身就包含了前面幾種):

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int N=1e5,mod=998244353,IG=332748118;
int n,lim,tra[4*N+10];
ll a[4*N+10],a1[4*N+10],b[4*N+10],c[4*N+10],d[4*N+10];
inline int Read()
{
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)&&ch!='-') ch=getchar();
	ch=='-'?f=-1:x=ch-'0';
	while(isdigit(ch=getchar())) x=x*10+ch-'0';
	return x*f;
}
inline ll ksm(ll x,int k)
{
	ll ret=1;
	while(k)
	{
		if(k&1) ret=ret*x%mod;
		x=x*x%mod,k>>=1;
	}
	return ret;
}
inline void ntt(ll *A,int opt)
{
	for(register int i=0;i<lim;++i) 
		if(i<tra[i]) swap(A[i],A[tra[i]]);
	for(register int i=2;i<=lim;i<<=1)
	{
		int len=i>>1;
		ll wn=ksm(opt==1?3:IG,(mod-1)/i);
		for(register int j=0;j<lim;j+=i)
		{
			ll buf=1;
			for(register int k=j;k<j+len;++k)
			{
				ll tmp=buf*A[k+len]%mod;
				A[k+len]=(A[k]-tmp+mod)%mod,A[k]=(A[k]+tmp)%mod;
				buf=buf*wn%mod;
			}
		}
	}
	if(opt==1) return;
	ll inv=ksm(lim,mod-2);
	for(register int i=0;i<lim;++i) A[i]=A[i]*inv%mod;
}
inline void p_inv(int depth,ll *A,ll *B)
{
	if(depth==1) {B[0]=ksm(A[0],mod-2);return;}
	p_inv((depth+1)>>1,A,B);
	lim=1;
	while(lim<2*depth) lim<<=1;
	for(register int i=0;i<lim;++i) tra[i]=(tra[i>>1]>>1)|((i&1)?lim>>1:0);
	for(register int i=0;i<depth;++i) c[i]=A[i];
	for(register int i=depth;i<lim;++i) c[i]=0;
	ntt(B,1),ntt(c,1);
	for(register int i=0;i<lim;++i) B[i]=(2ll-B[i]*c[i]%mod+mod)%mod*B[i]%mod;
	ntt(B,-1);
	for(register int i=depth;i<lim;++i) B[i]=0;
}
inline void p_ln(int n,ll *A,ll *B)
{
	for(register int i=0;i<=2*n;++i) a1[i]=B[i]=0;
	for(register int i=1;i<n;++i) a1[i-1]=i*A[i]%mod;a1[n-1]=0;
	p_inv(n,A,B);
	lim=1;
	while(lim<2*n) lim<<=1;
	for(register int i=0;i<lim;++i) tra[i]=(tra[i>>1]>>1)|((i&1)?lim>>1:0);
	ntt(a1,1),ntt(B,1);
	for(register int i=0;i<lim;++i) a1[i]=B[i]*a1[i]%mod;
	ntt(a1,-1);
	for(register int i=0;i<n-1;++i) B[i+1]=a1[i]*ksm(i+1,mod-2)%mod;B[0]=0;
}
inline void p_exp(int depth,ll *A,ll *B)
{
	if(depth==1) {B[0]=1;return;}
	p_exp((depth+1)>>1,A,B);
	lim=1;
	while(lim<2*depth) lim<<=1;
	for(register int i=0;i<lim;++i) tra[i]=(tra[i>>1]>>1)|((i&1)?lim>>1:0);
	p_ln(depth,B,d);
	for(register int i=0;i<depth;++i) d[i]=(A[i]-d[i]+mod)%mod;
	for(register int i=depth;i<lim;++i) d[i]=0;
	d[0]++;
	ntt(B,1),ntt(d,1);
	for(register int i=0;i<lim;++i) B[i]=d[i]*B[i]%mod;
	ntt(B,-1);
	for(register int i=depth;i<lim;++i) B[i]=0;
}
int main()
{
	n=Read();
	for(register int i=0;i<n;++i) a[i]=Read();
	p_exp(n,a,b);
	for(register int i=0;i<n;++i) printf("%lld ",b[i]);
	return 0;
}

一般來說這些是比較常用的(大概是吧),剩下的那些以後慢慢更新。