Luogu P4721 【模板】分治 FFT

多项式全家桶

运算法则 算法 时间复杂度
多项式乘法 快速傅里叶变换 Θ ( n log ⁡ 2 n ) \Theta(n\log_2 n) Θ(nlog2n)
多项式求逆 倍增+快速数论变换 Θ ( n log ⁡ 2 n ) \Theta(n\log_2 n) Θ(nlog2n)
多项式对数函数 求导+积分 Θ ( n log ⁡ 2 n ) \Theta(n\log_2 n) Θ(nlog2n)
多项式指数函数 泰勒展开+牛顿迭代 Θ ( n log ⁡ 2 n ) \Theta(n\log_2 n) Θ(nlog2n)
分治FFT卷积 分治FFT/多项式求逆 Θ ( n log ⁡ 2 2 n ) / Θ ( n log ⁡ 2 n ) \Theta(n\log_2^2 n)/\Theta(n\log_2 n) Θ(nlog22n)/Θ(nlog2n)

题目大意

%   给定长度为 n − 1 n-1 n1 的序列 g i g_i gi,其中 i ∈ [ 1 , n ) ∩ Z i\in [1,n)∩\Z i[1,n)Z,已知 f 0 = 1 f_0=1 f0=1,你需要对于每个 i i i 求出 f i = ∑ j = 1 i f i − j g j f_i=\sum_{j=1}^if_{i-j}g_j fi=j=1ifijgj  对 998244353 998244353 998244353 取模后的结果。

数据范围  2 ⩽ n ⩽ 1 0 5 , ∀ i ∈ [ 1 , n ) ∩ Z   ,    2\leqslant n\leqslant 10^5,\forall i\in[1,n)∩\Z\ ,\; 2n105,i[1,n)Z , 0 ⩽ g i < 998244353 0\leqslant g_i< 998244353 0gi<998244353

题解

%   先来考虑一种不那么通用,但效率更高的方法。
%   不妨令 g 0 = 0 g_0=0 g0=0,下标超出 n n n 的部分也为 0 0 0,设 f i f_i fi g i g_i gi 的生成函数分别为
F ( x ) = ∑ i = 0 ∞ f i x i G ( x ) = ∑ i = 0 ∞ g i x i \begin{aligned} F(x)=\sum_{i=0}^\infty f_ix^i\\ G(x)=\sum_{i=0}^\infty g_ix^i\\ \end{aligned} F(x)=i=0fixiG(x)=i=0gixi

%   则有(注意下标变化)
F ( x ) G ( x ) = ∑ i = 0 ∞ x i ( ∑ j = 0 i f i − j g j ) = ∑ i = 1 ∞ x i ( ∑ j = 1 i f i − j g j ) = ∑ i = 1 ∞ f i x i = ∑ i = 0 ∞ f i x i − f 0 = F ( x ) − f 0 \begin{aligned} F(x)G(x)&=\sum_{i=0}^\infty x^i\left(\sum_{j=0}^if_{i-j}g_j\right)\\ &=\sum_{i=1}^\infty x^i\left(\sum_{j=1}^if_{i-j}g_j\right)\\ &=\sum_{i=1}^\infty f_ix^i\\ &=\sum_{i=0}^\infty f_ix^i-f_0\\ &=F(x)-f_0\\ \end{aligned} F(x)G(x)=i=0xi(j=0ifijgj)=i=1xi(j=1ifijgj)=i=1fixi=i=0fixif0=F(x)f0

%   整理,得 F ( x ) = f 0 [ 1 − G ( x ) ] − 1 F(x)=f_0[1-G(x)]^{-1} F(x)=f0[1G(x)]1

%   因而有 F ( x ) ≡ f 0 [ 1 − G ( x ) ] − 1 ( m o d x n ) F(x)≡f_0[1-G(x)]^{-1}\pmod{x^n} F(x)f0[1G(x)]1(modxn)

%   然后就只需要一个多项式求逆即可。
  时间复杂度显然为 Θ ( n log ⁡ 2 n ) \Theta(n\log_2 n) Θ(nlog2n)

代码

#include
#include
#include
using namespace std;
#define MAXN 4000010
const long long mod=998244353,G=3,inG=332748118;
long long pows(long long a,long long b){
	long long ret=1;
	while(b){
		if(b&1) ret=ret*a%mod;
		b>>=1;a=a*a%mod;
	} return ret;
}
#define invt(x) pows(x,mod-2)
class poly{
	public:
	static const int maxn=MAXN;
	long long *t;
	int limit,l,inmit;
	long long& operator[](int x){return t[x];}
	const long long& operator[](int x)const{return t[x];}
	poly(int n=0):t(NULL),limit(1),l(0){init(n);}
	void init(int n){
		if(limit>=n) return;
		if(t) free(t);
		while(limit<n) limit<<=1,++l;
		inmit=invt(limit);
		t=(long long*)calloc(limit+5,sizeof(long long));
	}
	void fnt(int type){
		static long long r[MAXN];
		static int now=0;
		if(now!=limit&&(now=limit))
			for(int i=1;i<limit;i++)
				r[i]=((r[i>>1]>>1)|((i&1)<<(l-1)));
		for(int i=1;i<limit;i++)
			if(i<r[i]) swap(t[i],t[r[i]]);
		for(int mid=1;mid<limit;mid<<=1){
			long long wn=pows(type==1?G:inG,(mod-1)/(mid<<1));
			for(int j=0,B=(mid<<1);j<limit;j+=B){
				long long w=1;
				for(int k=0;k<mid;k++,w=w*wn%mod){
					int x=t[j+k],y=w*t[j+k+mid]%mod;
					t[j+k]=(x+y)%mod;t[j+k+mid]=(x-y+mod)%mod;
				}
			}
		} if(type==-1) for(int i=0;i<limit;i++)
			t[i]=t[i]*inmit%mod;
	}
	void inv(int n,poly &g)const{
		g.init(n+n);
		if(n==1) return (void)(g[0]=invt(t[0]));
		inv((n+1)>>1,g);
		poly a(n+n),b(n+n);
		for(int i=0;i<n;i++)
			a[i]=t[i],b[i]=g[i];
		a.fnt(1);b.fnt(1);
		for(int i=0;i<a.limit;i++)
			a[i]=b[i]*((2-a[i]*b[i]%mod+mod)%mod)%mod;
		a.fnt(-1);
		for(int i=0;i<n;i++) g[i]=a[i];
	}
}a,b;
int n;
int main(){
	scanf("%d",&n);
	a.init(n+n);b.init(n+n);
	for(int i=1;i<n;i++)
		scanf("%lld",&b[i]);
	for(int i=1;i<n;i++)
		b[i]=(mod-b[i])%mod;
	b[0]=1;b.inv(n,a);
	for(int i=0;i<n;i++)
		printf("%lld ",a[i]);
	return 0;
}

你可能感兴趣的:(分治FFT,快速数论变换)