分治FFT小结

模板题传送门

题目大意: 给出 g [ 1 ] , g [ 2 ] , . . . , g [ n − 1 ] g[1],g[2],...,g[n-1] g[1],g[2],...,g[n1],求 f [ 0 ] , f [ 1 ] , f [ 2 ] , . . . , f [ n − 1 ] f[0],f[1],f[2],...,f[n-1] f[0],f[1],f[2],...,f[n1],其中 f [ i ] = ∑ j = 1 i f [ i − j ] × g [ j ] f[i]=\sum_{j=1}^i f[i-j]\times g[j] f[i]=j=1if[ij]×g[j],边界为 f [ 0 ] = 1 f[0]=1 f[0]=1

正解有两种:一个是分治 F F T FFT FFT,另一个是多项式求逆(吊打分治 F F T FFT FFT),这里只讲分治 F F T FFT FFT

这里用到的分治,是神奇的 c d q cdq cdq 分治,用到的思想也就是计算左区间右区间的贡献。

发现对于 f [ i ] f[i] f[i],任意比 i i i 小的 j j j 都可以对它产生贡献,这样我们不妨进行分治,对于区间 [ l , m i d ] [l,mid] [l,mid],这个区间内的 f f f 肯定都能对区间 [ m i d + 1 , r ] [mid+1,r] [mid+1,r] 内的 f f f 产生贡献。

我们可以将区间 [ l , m i d ] [l,mid] [l,mid] 内的 f f f 拎出来,从 0 0 0 开始编号放到 A A A 数组里,发现下标最大的能产生贡献的 g g g g [ r − l ] g[r-l] g[rl] (从 g [ r − l + 1 ] g[r-l+1] g[rl+1] 开始,都不能使左区间对右区间产生贡献),于是我们将 g [ 1 ] g[1] g[1] ~ g [ r − l ] g[r-l] g[rl] 也拎出来,放到 B B B 数组里,然后将 A , B A,B A,B 跑一次 N T T NTT NTT,然后把卷出来的贡献累加到区间 [ m i d + 1 , r ] [mid+1,r] [mid+1,r] f f f 里即可。

代码如下:

#include 
#include 
#include 
#include 
using namespace std;
#define maxn 400010
#define ll long long
#define mod 998244353

int n;
ll ksm(int x,int y)
{
	ll re=1,tot=x;
	while(y>0)
	{
		if(y%2==1)re=re*tot%mod;
		tot=tot*tot%mod;
		y/=2;
	}
	return re;
}
ll inv(int x){return ksm(x,mod-2);}
int r[maxn];
void work(int len)
{
	int l=log2(len);
	for(int i=1;i<len;i++)
	r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
}
const int G=3,invG=332748118;
void ntt(ll *f,int len,int type)
{
	for(int i=1;i<len;i++)
	if(i<r[i])swap(f[i],f[r[i]]);
	for(int mid=1;mid<len;mid<<=1)
	{
		ll wn=ksm((type==1?G:invG),(mod-1)/mid/2);
		for(int block=mid<<1,j=0;j<len;j+=block)
		{
			ll w=1;
			for(int i=j;i<j+mid;i++,w=w*wn%mod)
			{
				ll x=f[i],y=f[i+mid]*w%mod;
				f[i]=(x+y)%mod;f[i+mid]=(x-y+mod)%mod;
			}
		}
	}
}
void NTT(ll *p1,ll *p2,int len)
{
	work(len);
	ntt(p1,len,1);ntt(p2,len,1);
	for(int i=0;i<len;i++)
	p1[i]=p1[i]*p2[i]%mod;
	ntt(p1,len,-1);
	ll invlen=inv(len);
	for(int i=0;i<len;i++)
	p1[i]=p1[i]*invlen%mod;
}
ll f[maxn],g[maxn];
ll a[maxn],b[maxn];
void fenzhi(int l,int r)
{
	if(l==r)return;
	int mid=l+r>>1;
	fenzhi(l,mid);
	int up=1;
	while(up<=(mid-l+r-l))up<<=1;
	for(int i=0;i<up;i++)
	a[i]=b[i]=0;
	for(int i=l;i<=mid;i++)a[i-l]=f[i];
	for(int i=1;i<=r-l;i++)b[i]=g[i];
	NTT(a,b,up);
	for(int i=mid+1;i<=r;i++)
	f[i]=(f[i]+a[i-l])%mod;
	fenzhi(mid+1,r);
}

int main()
{
	scanf("%d",&n);
	for(int i=1;i<n;i++)
	scanf("%lld",&g[i]);
	f[0]=1;
	fenzhi(0,n-1);
	for(int i=0;i<n;i++)
	printf("%lld ",f[i]);
}

可能还会更新的题表

洛谷 P4841 城市规划   题解
射命丸文的笔记   题解
[国家集训队]整数的lqp拆分   题解

你可能感兴趣的:(#,多项式)