【模板】分治FFT -cdq分治/多项式求逆

版权声明:欢迎转载(请附带原链接)ヾ(๑╹◡╹)ノ" https://blog.csdn.net/corsica6/article/details/84325973

传送门:luogu【模板】分治 FFT


题意

给定长度为 n 1 n-1 的数组 g [ 1 ] , g [ 2 ] , . . , g [ n 1 ] g[1],g[2],..,g[n-1] ,求 f [ 0 ] , f [ 1 ] , . . , f [ n 1 ] f[0],f[1],..,f[n-1] ,其中
f [ i ] = j = 1 i f [ i j ] g [ j ] f[i]=\sum\limits_{j=1}^if[i-j]g[j]
边界 f [ 0 ] = 1 f[0]=1 。答案模 998244353 998244353


题解

法1(cdq分治):

n t t ntt 模数当然用 n t t ntt 咯。

考虑后面的只与前面有关,可以 O ( n l o g 2 ) O(nlog^2) cdq分治。

假设已经求出 f [ i ] ( i [ l , m i d ] ) f[i](i\in[l,mid]) ,则这些答案对 f [ i ] ( i [ m i d + 1 , r ] ) f[i](i\in[mid+1,r]) 的贡献: w i = j = l m i d f [ j ] g [ i j ] w_i=\sum\limits_{j=l}^{mid}f[j]g[i-j] ,卷积计算即可。

#include<bits/stdc++.h>
using namespace std;
const int N=4e5+100,mod=998244353,gen=3;
typedef long long ll;

int n,f[N],g[N];
int a[N],b[N],ivg,len,rv[N];

inline int ad(int x,int y){x+=y;return x>=mod?x-mod:x;}
inline int dc(int x,int y){x-=y;return x<0?x+mod:x;}

inline int fp(int x,int y)
{
	int re=1;
	for(;y;y>>=1,x=(ll)x*x%mod)
	  if(y&1) re=(ll)re*x%mod;
	return re;
}

inline void ntt(int *c,int pr)
{
	int i,j,k,ori,pd,ix,iy,G=pr?gen:ivg;
	for(i=1;i<len;++i) if(i<rv[i]) swap(c[i],c[rv[i]]);
	for(i=1;i<len;i<<=1){
		ori=fp(G,(mod-1)/(i<<1));
		for(j=0;j<len;j+=(i<<1)){
			pd=1;
			for(k=0;k<i;++k,pd=(ll)pd*ori%mod){
				ix=c[j+k];iy=(ll)c[i+j+k]*pd%mod;
				c[j+k]=ad(ix,iy);c[i+j+k]=dc(ix,iy);
			}
		}
	}
	if(pr) return;
	G=fp(len,mod-2);
	for(i=0;i<len;++i) c[i]=(ll)c[i]*G%mod;
}

void cdq(int l,int r)
{
	if(l==r) return;
	int i,L=0,mx=(r-l+1)<<1,mid=(l+r)>>1;
	cdq(l,mid);
	for(len=1;len<mx;len<<=1) L++;
	for(i=1;i<len;++i) rv[i]=((rv[i>>1]>>1)|((i&1)<<(L-1)));
    for(i=l;i<=mid;++i) a[i-l]=f[i];
    for(i=mid+1-l;i<len;++i) a[i]=0;
    for(i=0,mx=r-l;i<=mx;++i) b[i]=g[i];
    for(i=mx+1;i<len;++i) b[i]=0;
    ntt(a,1);ntt(b,1);
    for(i=0;i<len;++i) a[i]=(ll)a[i]*b[i]%mod;
    ntt(a,0);
    for(i=mid+1;i<=r;++i) 
	  f[i]=ad(f[i],a[i-l]);
    cdq(mid+1,r);
}

int main(){
	int i;scanf("%d",&n);f[0]=1;ivg=fp(gen,mod-2);
	for(i=1;i<n;++i) scanf("%d",&g[i]);
	cdq(0,n-1);
	for(i=0;i<n;++i) printf("%d ",f[i]);
	return 0;
}

法2(多项式求逆):

f f 的生成函数长成这样:
f = f g + f 0 f=f*g+f_0

f 0 = 1 f_0=1 转化一下得到:
f ( 1 g ) 1 &VeryThinSpace; m o d &VeryThinSpace; x n f\equiv (1-g)^{-1}\bmod x^n

套多项式求逆模板即可 O ( n l o g n ) O(nlogn)

#include<bits/stdc++.h>
using namespace std;
const int N=4e5+100,mod=998244353,gen=3;
typedef long long ll;

int n,f[N],g[N];
int a[N],b[N],ivg,len,rv[N];

inline int ad(int x,int y){x+=y;return x>=mod?x-mod:x;}
inline int dc(int x,int y){x-=y;return x<0?x+mod:x;}

inline int fp(int x,int y)
{
	int re=1;
	for(;y;y>>=1,x=(ll)x*x%mod)
	  if(y&1) re=(ll)re*x%mod;
	return re;
}

inline void ntt(int *c,int pr)
{
	int i,j,k,ori,pd,ix,iy,G=pr?gen:ivg;
	for(i=1;i<len;++i) if(i<rv[i]) swap(c[i],c[rv[i]]);
	for(i=1;i<len;i<<=1){
		ori=fp(G,(mod-1)/(i<<1));
		for(j=0;j<len;j+=(i<<1)){
			pd=1;
			for(k=0;k<i;++k,pd=(ll)pd*ori%mod){
				ix=c[j+k];iy=(ll)c[i+j+k]*pd%mod;
				c[j+k]=ad(ix,iy);c[i+j+k]=dc(ix,iy);
			}
		}
	}
	if(pr) return;
	G=fp(len,mod-2);
	for(i=0;i<len;++i) c[i]=(ll)c[i]*G%mod;
}

void gtnv(int n,int *f,int *g)
{
	if(n==1) {g[0]=1;return;}
	gtnv((n+1)>>1,f,g);
	int i,j,L=0;
	for(len=1;len<n+n;len<<=1) L++;
	for(i=1;i<len;++i) rv[i]=((rv[i>>1]>>1)|((i&1)<<(L-1)));
	for(i=0;i<n;++i) a[i]=f[i];
	for(i=n;i<len;++i) a[i]=0;
	ntt(a,1);ntt(g,1);
	for(i=0;i<len;++i) g[i]=(ll)g[i]*dc(2,(ll)a[i]*g[i]%mod)%mod;
	ntt(g,0);
	for(i=n;i<len;++i) g[i]=0;
}

int main(){
	int i;scanf("%d",&n);ivg=fp(gen,mod-2);
	for(i=1;i<n;++i) {scanf("%d",&g[i]);if(g[i]) g[i]=mod-g[i];}
	g[0]=1;gtnv(n,g,f);
	for(i=0;i<n;++i) printf("%d ",f[i]);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/corsica6/article/details/84325973