多项式

  • 并没有讲解.只是记录用.

(FFT)

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int MAXN=4e6+10;
inline int read()
{
	int x=0;
	bool pos=1;
	char ch=getchar();
	for(; !isdigit(ch); ch=getchar())
		if(ch=='-')
			pos=0;
	for(; isdigit(ch); ch=getchar())
		x=x*10+ch-'0';
	return pos?x:-x;
}
struct cp
{
	double x,y;
	cp(double xx=0,double yy=0)
	{
		x=xx;
		y=yy;
	}
	cp operator +(const cp &b) const
	{
		return cp(x+b.x,y+b.y);
	}
	cp operator -(const cp &b) const
	{
		return cp(x-b.x,y-b.y);
	}
	cp operator *(const cp &b) const
	{
		return cp(x*b.x-y*b.y,x*b.y+y*b.x);
	}
};
const double PI=acos(-1.0);
int rev[MAXN];
void init(int n,int lim)
{
	for(int i=0; i<n; ++i)
	{
		for(int j=0; j<lim; ++j)
			if((i>>j)&1)
				rev[i]|=1<<(lim-j-1);
	}
}
void FFT(cp *a,int n,bool invflag)
{
	for(int i=0; i<n; ++i)
	{
		if(i<rev[i])
			swap(a[i],a[rev[i]]);
	}
	for(int l=2; l<=n; l<<=1)
	{
		int m=l>>1;
		cp wi=cp(cos(2*PI/l),sin(2*PI/l));
		if(invflag)
			wi=cp(cos(2*PI/l),-sin(2*PI/l));
		for(cp *p=a; p!=a+n; p+=l)
		{
			cp w=cp(1,0);
			for(int i=0; i<m; ++i)
			{
				cp t=w*p[i+m];
				p[i+m]=p[i]-t;
				p[i]=p[i]+t;
				w=w*wi;
			}
		}
	}
	if(invflag)
	{
		for(int i=0; i<n; ++i)
			a[i].x/=n,a[i].y/=n;
	}
}
int n,m;
cp a[MAXN],b[MAXN];
int main()
{
	n=read(),m=read();
	++n,++m;
	for(int i=0; i<n; ++i)
		a[i]=cp((double)read(),0);
	for(int i=0; i<m; ++i)
		b[i]=cp((double)read(),0);
	int N=1,lim=0;
	while(N<n+m-1)
		N<<=1,++lim;
	init(N,lim);
	FFT(a,N,false);
	FFT(b,N,false);
	for(int i=0; i<N; ++i)
		a[i]=a[i]*b[i];
	FFT(a,N,true);
	for(int i=0; i<n+m-1; ++i)
		printf("%d ",(int)(a[i].x+0.5));
	return 0;
}

(NTT)

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int MAXN=4e6+10;
inline int read()
{
	int x=0;
	bool pos=1;
	char ch=getchar();
	for(; !isdigit(ch); ch=getchar())
		if(ch=='-')
			pos=0;
	for(; isdigit(ch); ch=getchar())
		x=x*10+ch-'0';
	return pos?x:-x;
}
const int P=998244353,G=3;
int add(int a,int b)
{
	return (a + b) % P;
}
int mul(int a,int b)
{
	return 1LL * a * b % P;
}
int fpow(int a,int b)
{
	int res=1;
	while(b)
	{
		if(b&1)
			res=mul(res,a);
		a=mul(a,a);
		b>>=1;
	}
	return res;
}
int inv(int x)
{
	return fpow(x,P-2);
}
int rev[MAXN];
void init(int n,int lim)
{
	for(int i=0; i<n; ++i)
	{
		for(int j=0; j<lim; ++j)
			if((i>>j)&1)
				rev[i]|=1<<(lim-j-1);
	}
}
void NTT(int *a,int n,bool invflag)
{
	for(int i=0; i<n; ++i)
	{
		if(i<rev[i])
			swap(a[i],a[rev[i]]);
	}
	for(int l=2; l<=n; l<<=1)
	{
		int gi=fpow(G,(P-1)/l);
		if(invflag)
			gi=inv(gi);
		int m=l>>1;
		for(int *p=a;p!=a+n;p+=l)
		{
			int g=1;
			for(int i=0; i<m; ++i)
			{
				int t=mul(g,p[i+m]);
				p[i+m]=add(p[i],P-t);
				p[i]=add(p[i],t);
				g=mul(g,gi);
			}
		}
	}
	if(invflag)
	{
		int Invn=inv(n);
		for(int i=0; i<n; ++i)
			a[i]=mul(a[i],Invn);
	}
}
int n,m;
int a[MAXN],b[MAXN];
int main()
{
	n=read(),m=read();
	++n,++m;
	for(int i=0; i<n; ++i)
		a[i]=read();
	for(int i=0; i<m; ++i)
		b[i]=read();
	int N=1,lim=0;
	while(N<n+m-1)
		N<<=1,++lim;
	init(N,lim);
	NTT(a,N,false);
	NTT(b,N,false);
	for(int i=0; i<N; ++i)
		a[i]=mul(a[i],b[i]);
	NTT(a,N,true);
	for(int i=0; i<n+m-1; ++i)
		printf("%d ",a[i]);
	return 0;
}

(FWT)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
inline int read()
{
	int out=0,fh=1;
	char jp=getchar();
	while ((jp>'9'||jp<'0')&&jp!='-')
		jp=getchar();
	if (jp=='-')
		fh=-1,jp=getchar();
	while (jp>='0'&&jp<='9')
		out=out*10+jp-'0',jp=getchar();
	return out*fh;
}
const int P=998244353,inv2=(P+1)>>1;
inline int add(int a,int b)
{
	return (a + b) % P;
}
inline int mul(int a,int b)
{
	return 1LL * a * b % P;
}
void FWT(int *a,int n,int op)
{
	for(int l=2;l<=n;l<<=1)
	{
		int m=l>>1;
		for(int *p=a;p!=a+n;p+=l)
			for(int i=0;i<m;++i)
			{
				int x0=p[i],x1=p[i+m];
				if(op==1)//or
					p[i]=x0,p[i+m]=add(x0,x1);
				else if(op==2)//and
					p[i]=add(x0,x1),p[i+m]=x1;
				else//xor
					p[i]=add(x0,x1),p[i+m]=add(x0,P-x1);
			}
	}
}
void IFWT(int *a,int n,int op)
{
	for(int l=2;l<=n;l<<=1)
	{
		int m=l>>1;
		for(int *p=a;p!=a+n;p+=l)
			for(int i=0;i<m;++i)
			{
				int x0=p[i],x1=p[i+m];
				if(op==1)
					p[i]=x0,p[i+m]=add(x1,P-x0);
				else if(op==2)
					p[i]=add(x0,P-x1),p[i+m]=x1;
				else
					p[i]=mul(add(x0,x1),inv2),p[i+m]=mul(add(x0,P-x1),inv2);
			}
	}
}
const int MAXN=(1<<17)+10;
int a[MAXN],b[MAXN],c[MAXN];
int main()
{
	int n=read();
	n=1<<n;
	for(int i=0;i<n;++i)
		a[i]=read();
	for(int i=0;i<n;++i)
		b[i]=read();
	for(int op=1;op<=3;++op)
	{
		FWT(a,n,op);
		FWT(b,n,op);
		for(int i=0;i<n;++i)
			c[i]=mul(a[i],b[i]);
		IFWT(a,n,op);
		IFWT(b,n,op);
		IFWT(c,n,op);
		for(int i=0;i<n;++i)
			printf("%d ",c[i]);
		puts("");
	}
	return 0;
}

三模数 (NTT)

  • 取三个具有 (P=pcdot 2^k+1) 形式的模数,在每个模意义下分别求解,最后用 (CRT​) 合并.这样做要求最终答案不超过三个模数之积.

拆系数 (FFT)

  • 对任意模数 (P) ,记 (m=sqrt P).那么把每个系数写成 (a imes m+b,a,b<m) 的形式,然后乘法分配律分别做,这样每个系数都不会超过 (P) ,最后再合并.

分治 (NTT/FFT)

  • 现在要算这样一个形式的卷积:

[f_i=sum_{j=1}^{i}f_{i-j}cdot g_j ]

  • 给出初始值 (f_0) 以及 (g) 的所有值,求 (f) 的所有项.
  • 如果每次直接做 (NTT/FFT) ,复杂度显然为 (O(n^2logn)) ,爆炸.
  • 考虑一个像 (cdq) 分治的东西,即算左边,再算左边对右边的贡献,再算右边,长度为 (1) 时返回.
  • 若当前计算区间 ([l,r)) ,就把 (f) 的前 (frac {r-l} 2) 项与 (g) 的前 (r-l) 项做卷积,把答案的后 (frac {r-l} 2) 项加入 (f) 的对应位置.
  • 这样做后半段一定是对的,所以就不用关注长度的问题了.时间复杂度为 (O(nlog^2n)) .

分治 (NTT)

#include<bits/stdc++.h>
using namespace std;
#define ll long long
inline ll read()
{
    ll out=0,fh=1;
    char jp=getchar();
    while ((jp>'9'||jp<'0')&&jp!='-')
        jp=getchar();
    if (jp=='-')
        fh=-1,jp=getchar();
    while (jp>='0'&&jp<='9')
        out=out*10+jp-'0',jp=getchar();
    return out*fh;
}
const int P=998244353,G=3;
inline int add(int a,int b)
{
	return (a + b) % P;
}
inline int mul(int a,int b)
{
	return 1LL * a * b % P;
}
int fpow(int a,int b)
{
	int res=1;
	while(b)
	{
		if(b&1)
			res=mul(res,a);
		a=mul(a,a);
		b>>=1;
	}
	return res;
}
const int MAXN=2e5+10;
int rev[MAXN],omega[MAXN],inv[MAXN];
void rev_init(int n,int lim)
{
	for(int i=0;i<n;++i)
	{
		int t=0;
		for(int j=0;j<lim;++j)
			if((i>>j)&1)
				t|=1<<(lim-j-1);
		rev[i]=t;
	}
}
void omega_init(int n)
{
	for(int l=2;l<=n;l<<=1)
	{
		omega[l]=fpow(G,(P-1)/l);
		inv[l]=fpow(omega[l],P-2);
	}
}
void NTT(int *a,int n,int lim,int invflag)
{
	for(int i=0;i<n;++i)
	{
		if(i<rev[i])
			swap(a[i],a[rev[i]]);
	}
	for(int l=2;l<=n;l<<=1)
	{
		int m=l>>1;
		int gi=omega[l];
		if(invflag)
			gi=inv[l];
		for(int *p=a;p!=a+n;p+=l)
		{
			int g=1;
			for(int i=0;i<m;++i)
			{
				int t=mul(g,p[i+m]);
				p[i+m]=add(p[i],P-t);
				p[i]=add(p[i],t);
				g=mul(g,gi);
			}
		}
	}
	if (invflag)
	{
		int invn=fpow(n,P-2);
		for(int i=0;i<n;++i)
			a[i]=mul(a[i],invn);
	}
}
int f[MAXN],g[MAXN],a[MAXN],b[MAXN];
void solve(int l,int r,int lim)//[l,r)
{
	if(lim<=0)
		return;
	int mid=(l+r)>>1,len=r-l;
	solve(l,mid,lim-1);
	rev_init(len,lim);
	for(int i=0;i<len/2;++i)
		a[i]=f[l+i];
	for(int i=len/2;i<len;++i)
		a[i]=0;
	for(int i=0;i<len;++i)
		b[i]=g[i];
	NTT(a,len,lim,false);
	NTT(b,len,lim,false);
	for(int i=0;i<len;++i)
		a[i]=mul(a[i],b[i]);
	NTT(a,len,lim,true);
	for(int i=len/2;i<len;++i)
		f[i+l]=add(f[i+l],a[i]);
	solve(mid,r,lim-1);
}
int main()
{
	int n=read();
	f[0]=1;
	for(int i=1;i<n;++i)
		g[i]=read();
	int N=1,lim=0;
	while(N<n)
		N<<=1,++lim;
	omega_init(N);
	solve(0,N,lim);	
	for(int i=0;i<n;++i)
		printf("%d ",f[i]);
	puts("");
    return 0;
}

多项式求逆

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int read()
{
	int out=0,sgn=1;
	char jp=getchar();
	while(jp!='-' && (jp<'0' || jp>'9'))
		jp=getchar();
	if(jp=='-')
		sgn=-1,jp=getchar();
	while(jp>='0' && jp<='9')
		out=out*10+jp-'0',jp=getchar();
	return out*sgn;
}
const int MAXN=4e5+10;
const int P=998244353,G=3;
int add(int a,int b)
{
	return (a+b>=P)?(a+b-P):(a+b);
}
int mul(int a,int b)
{
	return 1LL * a * b % P;
}
int fpow(int a,int b)
{
	int res=1;
	while(b)
	{
		if(b&1)
			res=mul(res,a);
		a=mul(a,a);
		b>>=1;
	}
	return res;
}
int rev[MAXN],omega[MAXN],inv[MAXN];
int curn;
void init(int n)
{
	for(int i=0;i<n;++i)
		rev[i]=(rev[i>>1]>>1)|((i&1)*(n>>1));
	for(int l=2; l<=n; l<<=1)
	{
		omega[l]=fpow(G,(P-1)/l);
		inv[l]=fpow(omega[l],P-2);
	}
	curn=n;
}
void DFT(int *a,int n,bool invflag)
{
	if(curn!=n)
		init(n);
	for(int i=0; i<n; ++i)
		if(i<rev[i])
			swap(a[i],a[rev[i]]);
	for(int l=2; l<=n; l<<=1)
	{
		int m=(l>>1);
		int gi=omega[l];
		if(invflag)
			gi=inv[l];
		for(int *p=a; p!=a+n; p+=l)
		{
			int g=1;
			for(int i=0; i<m; ++i)
			{
				int t=mul(g,p[i+m]);
				p[i+m]=add(p[i],P-t);
				p[i]=add(p[i],t);
				g=mul(g,gi);
			}
		}
	}
	if(invflag)
	{
		int invn=fpow(n,P-2);
		for(int i=0; i<n; ++i)
			a[i]=mul(a[i],invn);
	}
}
int a[MAXN],b[MAXN];
void NTT(int *A,int *B,int *C,int lenA,int lenB)
{
	int lenC=lenA+lenB-1,n=1;
	while(n<lenC)
		n<<=1;
	for(int i=0;i<lenA;++i)
		a[i]=A[i];
	for(int i=lenA;i<n;++i)
		a[i]=0;
	for(int i=0;i<lenB;++i)
		b[i]=B[i];
	for(int i=lenB;i<n;++i)
		b[i]=0;
	DFT(a,n,false);
	DFT(b,n,false);
	for(int i=0; i<n; ++i)
		C[i]=mul(a[i],b[i]);
	DFT(C,n,true);
}
int tmp[MAXN];
void poly_inverse(int *A,int *B,int n)
{
	B[0]=fpow(A[0],P-2);
	int k=0;
	for(int i=2;i<=n;i<<=1)
	{
		++k;
		NTT(A,B,tmp,i,i);
		NTT(tmp,B,tmp,i,i);
		for(int j=0;j<i;++j)
			B[j]=add(mul(2,B[j]),P-tmp[j]);
	}
}
int poly[MAXN],invpoly[MAXN];
int main()
{
	int n=read();
	for(int i=0;i<n;++i)
		poly[i]=read();
	int N=1,lim=0;
	while((1<<lim)<n)
		++lim,N<<=1;
	poly_inverse(poly,invpoly,N);
	for(int i=0;i<n;++i)
		printf("%d ",invpoly[i]);
	puts("");
	return 0;
}

多项式除法/多项式取模

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int read()
{
	int out=0,sgn=1;
	char jp=getchar();
	while(jp!='-' && (jp<'0' || jp>'9'))
		jp=getchar();
	if(jp=='-')
		sgn=-1,jp=getchar();
	while(jp>='0' && jp<='9')
		out=out*10+jp-'0',jp=getchar();
	return out*sgn;
}
const int MAXN=4e5+10;
const int P=998244353,G=3;
int add(int a,int b)
{
	return (a+b>=P)?(a+b-P):(a+b);
}
int mul(int a,int b)
{
	return 1LL * a * b % P;
}
int fpow(int a,int b)
{
	int res=1;
	while(b)
	{
		if(b&1)
			res=mul(res,a);
		a=mul(a,a);
		b>>=1;
	}
	return res;
}
int rev[MAXN],omega[MAXN],inv[MAXN];
int curn;
void init(int n)
{
	for(int i=0;i<n;++i)
		rev[i]=(rev[i>>1]>>1)|((i&1)*(n>>1));
	for(int l=2; l<=n; l<<=1)
	{
		omega[l]=fpow(G,(P-1)/l);
		inv[l]=fpow(omega[l],P-2);
	}
	curn=n;
}
void DFT(int *a,int n,bool invflag)
{
	if(curn!=n)
		init(n);
	for(int i=0; i<n; ++i)
		if(i<rev[i])
			swap(a[i],a[rev[i]]);
	for(int l=2; l<=n; l<<=1)
	{
		int m=(l>>1);
		int gi=omega[l];
		if(invflag)
			gi=inv[l];
		for(int *p=a; p!=a+n; p+=l)
		{
			int g=1;
			for(int i=0; i<m; ++i)
			{
				int t=mul(g,p[i+m]);
				p[i+m]=add(p[i],P-t);
				p[i]=add(p[i],t);
				g=mul(g,gi);
			}
		}
	}
	if(invflag)
	{
		int invn=fpow(n,P-2);
		for(int i=0; i<n; ++i)
			a[i]=mul(a[i],invn);
	}
}
int a[MAXN],b[MAXN];
void NTT(int *A,int *B,int *C,int lenA,int lenB)
{
	int lenC=lenA+lenB-1,n=1;
	while(n<lenC)
		n<<=1;
	for(int i=0;i<lenA;++i)
		a[i]=A[i];
	for(int i=lenA;i<n;++i)
		a[i]=0;
	for(int i=0;i<lenB;++i)
		b[i]=B[i];
	for(int i=lenB;i<n;++i)
		b[i]=0;
	DFT(a,n,false);
	DFT(b,n,false);
	for(int i=0; i<n; ++i)
		C[i]=mul(a[i],b[i]);
	DFT(C,n,true);
}
int tmp[MAXN];
void poly_inverse(int *A,int *B,int n)
{
	for(int i=0;i<2*n;++i)
		B[i]=0;
	B[0]=fpow(A[0],P-2);
	int k=0;
	for(int i=2;i<=n;i<<=1)
	{
		++k;
		NTT(A,B,tmp,i,i);
		NTT(tmp,B,tmp,i,i);
		for(int j=0;j<i;++j)
			B[j]=add(mul(2,B[j]),P-tmp[j]);
	}
}
void poly_rev(int *A,int n)//n->最高项次数 
{
	for(int i=0;i<n-i;++i)
		swap(A[i],A[n-i]);
}
int InvB[MAXN];
int ModA[MAXN],ModB[MAXN];
void poly_division(int *A,int *B,int *D,int *R,int n,int m)
{
	poly_rev(A,n);
	poly_rev(B,m);
	for(int i=0;i<n-m+1;++i)
		ModA[i]=A[i],ModB[i]=B[i];
	int N=1;
	while(N<n-m+1)
		N<<=1;
	poly_inverse(ModB,InvB,N);
	for(int i=n-m+1;i<N;++i)
		InvB[i]=0;
	NTT(ModA,InvB,D,n-m+1,n-m+1);
	poly_rev(D,n-m);
	poly_rev(A,n);
	poly_rev(B,m);
	NTT(B,D,R,m,n-m+1);
	for(int i=0;i<n;++i)
		R[i]=add(A[i],P-R[i]);
}
int A[MAXN],B[MAXN],D[MAXN],R[MAXN];
int main()
{
	int n=read(),m=read();
	for(int i=0;i<=n;++i)
		A[i]=read();
	for(int i=0;i<=m;++i)
		B[i]=read();
	poly_division(A,B,D,R,n,m);
	for(int i=0;i<n-m+1;++i)
		printf("%d ",D[i]);
	puts("");
	for(int i=0;i<m;++i)
		printf("%d ",R[i]);
	puts("");
	return 0;
}

多项式 (ln)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int read()
{
	int out=0,sgn=1;
	char jp=getchar();
	while(jp!='-' && (jp<'0' || jp>'9'))
		jp=getchar();
	if(jp=='-')
		sgn=-1,jp=getchar();
	while(jp>='0' && jp<='9')
		out=out*10+jp-'0',jp=getchar();
	return out*sgn;
}
const int MAXN=4e5+10;
const int P=998244353,G=3;
int add(int a,int b)
{
	return (a+b>=P)?(a+b-P):(a+b);
}
int mul(int a,int b)
{
	return 1LL * a * b % P;
}
int fpow(int a,int b)
{
	int res=1;
	while(b)
	{
		if(b&1)
			res=mul(res,a);
		a=mul(a,a);
		b>>=1;
	}
	return res;
}
int rev[MAXN],omega[MAXN],inv[MAXN];
int curn;
void init(int n)
{
	for(int i=0;i<n;++i)
		rev[i]=(rev[i>>1]>>1)|((i&1)*(n>>1));
	for(int l=2; l<=n; l<<=1)
	{
		omega[l]=fpow(G,(P-1)/l);
		inv[l]=fpow(omega[l],P-2);
	}
	curn=n;
}
void DFT(int *a,int n,bool invflag)
{
	if(curn!=n)
		init(n);
	for(int i=0; i<n; ++i)
		if(i<rev[i])
			swap(a[i],a[rev[i]]);
	for(int l=2; l<=n; l<<=1)
	{
		int m=(l>>1);
		int gi=omega[l];
		if(invflag)
			gi=inv[l];
		for(int *p=a; p!=a+n; p+=l)
		{
			int g=1;
			for(int i=0; i<m; ++i)
			{
				int t=mul(g,p[i+m]);
				p[i+m]=add(p[i],P-t);
				p[i]=add(p[i],t);
				g=mul(g,gi);
			}
		}
	}
	if(invflag)
	{
		int invn=fpow(n,P-2);
		for(int i=0; i<n; ++i)
			a[i]=mul(a[i],invn);
	}
}
int a[MAXN],b[MAXN];
void NTT(int *A,int *B,int *C,int lenA,int lenB)
{
	int lenC=lenA+lenB-1,n=1;
	while(n<lenC)
		n<<=1;
	for(int i=0;i<lenA;++i)
		a[i]=A[i];
	for(int i=lenA;i<n;++i)
		a[i]=0;
	for(int i=0;i<lenB;++i)
		b[i]=B[i];
	for(int i=lenB;i<n;++i)
		b[i]=0;
	DFT(a,n,false);
	DFT(b,n,false);
	for(int i=0; i<n; ++i)
		C[i]=mul(a[i],b[i]);
	DFT(C,n,true);
}
int tmp[MAXN];
void poly_inverse(int *A,int *B,int n)
{
	for(int i=0;i<2*n;++i)
		B[i]=0;
	B[0]=fpow(A[0],P-2);
	int k=0;
	for(int i=2;i<=n;i<<=1)
	{
		++k;
		NTT(A,B,tmp,i,i);
		NTT(tmp,B,tmp,i,i);
		for(int j=0;j<i;++j)
			B[j]=add(mul(2,B[j]),P-tmp[j]);
	}
}
void poly_diff(int *A,int n)
{
	for(int i=0;i<n-1;++i)
		A[i]=mul(A[i+1],i+1);
}
void poly_int(int *A,int n)
{
	for(int i=n;i>=1;--i)
		A[i]=mul(A[i-1],fpow(i,P-2));
}
int InvA[MAXN];
void ln(int *A,int *B,int n)
{
	poly_inverse(A,InvA,n);
	poly_diff(A,n);
	NTT(A,InvA,B,n,n);
	poly_int(B,n);
	B[0]=0;
}
int A[MAXN],B[MAXN];
int main()
{
	int n=read();
	int N=1;
	while(N<n)
		N<<=1;
	for(int i=0;i<n;++i)
		A[i]=read();
	ln(A,B,N);
	for(int i=0;i<n;++i)
		printf("%d ",B[i]);
	puts("");
	return 0;
}

多项式 (exp)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int read()
{
	int out=0,sgn=1;
	char jp=getchar();
	while(jp!='-' && (jp<'0' || jp>'9'))
		jp=getchar();
	if(jp=='-')
		sgn=-1,jp=getchar();
	while(jp>='0' && jp<='9')
		out=out*10+jp-'0',jp=getchar();
	return out*sgn;
}
const int MAXN=4e5+10;
const int P=998244353,G=3;
int add(int a,int b)
{
	return (a+b>=P)?(a+b-P):(a+b);
}
int mul(int a,int b)
{
	return 1LL * a * b % P;
}
int fpow(int a,int b)
{
	int res=1;
	while(b)
	{
		if(b&1)
			res=mul(res,a);
		a=mul(a,a);
		b>>=1;
	}
	return res;
}
int rev[MAXN],omega[MAXN],inv[MAXN];
int curn;
void init(int n)
{
	for(int i=0;i<n;++i)
		rev[i]=(rev[i>>1]>>1)|((i&1)*(n>>1));
	for(int l=2; l<=n; l<<=1)
	{
		omega[l]=fpow(G,(P-1)/l);
		inv[l]=fpow(omega[l],P-2);
	}
	curn=n;
}
void DFT(int *a,int n,bool invflag)
{
	if(curn!=n)
		init(n);
	for(int i=0; i<n; ++i)
		if(i<rev[i])
			swap(a[i],a[rev[i]]);
	for(int l=2; l<=n; l<<=1)
	{
		int m=(l>>1);
		int gi=omega[l];
		if(invflag)
			gi=inv[l];
		for(int *p=a; p!=a+n; p+=l)
		{
			int g=1;
			for(int i=0; i<m; ++i)
			{
				int t=mul(g,p[i+m]);
				p[i+m]=add(p[i],P-t);
				p[i]=add(p[i],t);
				g=mul(g,gi);
			}
		}
	}
	if(invflag)
	{
		int invn=fpow(n,P-2);
		for(int i=0; i<n; ++i)
			a[i]=mul(a[i],invn);
	}
}
int a[MAXN],b[MAXN];
void NTT(int *A,int *B,int *C,int lenA,int lenB)
{
	int lenC=lenA+lenB-1,n=1;
	while(n<lenC)
		n<<=1;
	for(int i=0;i<lenA;++i)
		a[i]=A[i];
	for(int i=lenA;i<n;++i)
		a[i]=0;
	for(int i=0;i<lenB;++i)
		b[i]=B[i];
	for(int i=lenB;i<n;++i)
		b[i]=0;
	DFT(a,n,false);
	DFT(b,n,false);
	for(int i=0; i<n; ++i)
		C[i]=mul(a[i],b[i]);
	DFT(C,n,true);
}
int tmp[MAXN];
void poly_inverse(int *A,int *B,int n)
{
	for(int i=0;i<2*n;++i)
		B[i]=0;
	B[0]=fpow(A[0],P-2);
	int k=0;
	for(int i=2;i<=n;i<<=1)
	{
		++k;
		NTT(A,B,tmp,i,i);
		NTT(tmp,B,tmp,i,i);
		for(int j=0;j<i;++j)
			B[j]=add(mul(2,B[j]),P-tmp[j]);
	}
}
void poly_diff(int *A,int n)
{
	for(int i=0;i<n-1;++i)
		A[i]=mul(A[i+1],i+1);
}
void poly_int(int *A,int n)
{
	for(int i=n;i>=1;--i)
		A[i]=mul(A[i-1],fpow(i,P-2));
}
int InvA[MAXN],tmpA[MAXN];
void poly_ln(int *A,int *B,int n)
{
	for(int i=0;i<n;++i)
		tmpA[i]=A[i];
	poly_inverse(tmpA,InvA,n);
	poly_diff(tmpA,n);
	NTT(tmpA,InvA,B,n,n);
	poly_int(B,n);
	B[0]=0;
	for(int i=0;i<n;++i)
		tmpA[i]=0;
}
int lnB[MAXN];
void poly_exp(int *A,int *B,int n)
{
	if(n==1)
	{
		B[0]=1;
		return;
	}
	poly_exp(A,B,n>>1);
	for(int i=0;i<n;++i)
		lnB[i]=0;
	poly_ln(B,lnB,n);
	for(int i=0;i<n;++i)
		lnB[i]=add(A[i],P-lnB[i]);
	lnB[0]=add(lnB[0],1);
	NTT(B,lnB,B,n>>1,n);
	for(int i=n;i<(n+(n>>1));++i)
		B[i]=0;		
}
int A[MAXN],B[MAXN];
int main()
{
	int n=read();
	for(int i=0;i<n;++i)
		A[i]=read();
	int N=1;
	while(N<n)
		N<<=1;
	poly_exp(A,B,N);
	for(int i=0;i<n;++i)
		printf("%d ",B[i]);
	puts("");
	return 0;
}

原文地址:https://www.cnblogs.com/jklover/p/10666102.html