【洛谷4721】【模板】分治 FFT

点此看题面

大致题意: 给定\(g_{1\sim n-1}\),求\(f_{0\sim n-1}\),满足\(f_0=1,f_i=\sum_{j=1}^if_{i-j}g_j(i>0)\)。

大致思路

话说为什么分治FFT的板子要求写的却是分治NTT。

考虑\(CDQ\)分治,每次先处理左区间,然后求出左区间对右区间的贡献(做范围为区间长度的卷积),接着再递归处理右区间。

方便起见可以把一开始给定的\(n\)变成\(2\)的幂。

其实也挺简单的,主要是当个模板题来写一写。。。

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
#define X 998244353
using namespace std;
int n,f[2*N+5],g[2*N+5];
I int QP(RI x,RI y) {RI t=1;W(y) y&1&&(t=1LL*t*x%X),x=1LL*x*x%X,y>>=1;return t;}
namespace Poly
{
	#define Init(n) P=1,L=0;W(P<=2*(n)) P<<=1,++L;\
		for(i=0;i^P;++i) A[i]=B[i]=0,R[i]=(R[i>>1]>>1)|((i&1)<<L-1);//多项式卷积初始化
	int PR=3,IPR=QP(3,X-2),P,L,A[4*N+5],B[4*N+5],R[4*N+5];
	I void NTT(int *s,CI t)//NTT
	{
		RI i,j,k,x,y,U,S;for(i=0;i^P;++i) i<R[i]&&(x=s[i],s[i]=s[R[i]],s[R[i]]=x);
		for(i=1;i^P;i<<=1) for(U=QP(t,X/(i<<1)),j=0;j^P;j+=i<<1) for(S=1,k=0;k^i;
			S=1LL*S*U%X,++k) s[j+k]=((x=s[j+k])+(y=1LL*S*s[i+j+k]%X))%X,s[i+j+k]=(x-y+X)%X;
	}
	I void CDQ_NTT(CI l,CI r,int *f,int *g)//分治NTT
	{
		if(l==r) return (void)(!l&&(f[l]=1));RI i,mid=l+r>>1;CDQ_NTT(l,mid,f,g);//先处理左区间
		Init(r-l);for(i=0;i<=mid-l;++i) A[i]=f[l+i];for(i=0;i<=r-l;++i) B[i]=g[i];//NTT范围为区间长度
		for(NTT(A,PR),NTT(B,PR),i=0;i^P;++i) A[i]=1LL*A[i]*B[i]%X;RI t=QP(P,X-2);//卷起来
		for(NTT(A,IPR),i=mid+1;i<=r;++i) f[i]=(1LL*A[i-l]*t+f[i])%X;CDQ_NTT(mid+1,r,f,g);//用左区间更新右区间信息,然后处理右区间
	}
}
int main()
{
	RI i;for(scanf("%d",&n),--n,i=1;i<=n;++i) scanf("%d",g+i);//读入
	RI m=1;W(m<=n) m<<=1;for(Poly::CDQ_NTT(0,m-1,f,g),i=0;i<=n;++i) printf("%d ",f[i]);return 0;//输出
}
上一篇:【洛谷6689】序列(组合数学)


下一篇:Luogu3178 [HAOI2015]树上操作