【模板】分治 FFT

题目

传送门

题目背景
也可用多项式求逆解决。

题目描述
给定序列 g_{1\dots n - 1}g
1…n−1

,求序列 f_{0\dots n - 1}f
0…n−1

其中 f_i=\sum_{j=1}^if_{i-j}g_jf
i

=∑
j=1
i

f
i−j

g
j

,边界为 f_0=1f
0

=1。

答案对 998244353998244353 取模。

输入格式
第一行一个整数 nn 。

第二行 n-1n−1 个整数 g_{1\dots n - 1}g
1…n−1

输出格式
一行 nn 个整数,表示 f_{0\dots n - 1}f
0…n−1

对 998244353998244353 取模后的值。

输入输出样例
输入 #1 复制
4
3 1 2
输出 #1 复制
1 3 10 35
输入 #2 复制
10
2 456 32 13524543 998244352 0 1231 634544 51
输出 #2 复制
1 2 460 1864 13738095 55389979 617768468 234028967 673827961 708520894
说明/提示
2\leq n\leq 10^52≤n≤10
5
,0\leq g_i<9982443530≤g
i

<998244353。

思路

用cdq分治+NTT

每次处理[l,mid]对[mid+1,r]的贡献

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=4e5+77,mod=998244353,G=3;
int lg=0,len=1;
ll a[N],b[N],g[N],f[N];
int rev[N];
ll power(ll x,ll t)
{
	ll b=1;
	while(t)
	{
		if(t&1) b=b*x%mod;
		x=x*x%mod; t>>=1;
	}
	return b;
}
void dft(ll *a,int n,int aii)
{
	for(int i=0; i<n; i++) if(i<rev[i]) swap(a[i],a[rev[i]]);
	for(int i=2; i<=n; i<<=1)
	{
		int now=i>>1;
		ll wn=power(G,(mod-1)/i);
		if(aii==-1) wn=power(wn,mod-2);
		for(int j=0; j<n; j+=i)
		{
			ll w=1,x,y;
			for(int k=j; k<j+now; k++,w=w*wn%mod)
			{
				x=a[k],y=w*a[k+now]%mod;
				a[k]=(x+y)%mod; a[k+now]=(x-y+mod)%mod;
			}
		}
	}
	if(aii==-1) for(int i=0; i<=n; i++) a[i]=a[i]*power(n,mod-2)%mod;
}
void NTT(ll *a,ll *b,int n,int m)
{
	dft(a,len,1); dft(b,len,1);
	for(int i=0; i<=len; i++) a[i]=a[i]*b[i]%mod;
	dft(a,len,-1);
}
void cdq(int l,int r)
{
	if(l==r) return;
	int mid=(l+r)>>1;
	cdq(l,mid);
	int n=r-l+1; len=1; lg=0;
	while(len<=n)
	{
		len<<=1; lg++;
	}
	for(int i=0; i<len; i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(lg-1)),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-1]=g[i];
	NTT(a,b,mid-l+1,r-l);
	for(int i=mid+1; i<=r; i++) f[i]=(f[i]+a[i-l-1])%mod;
	cdq(mid+1,r);
}
int n;
int main()
{
	scanf("%d",&n);
	for(int i=1; i<n; i++) scanf("%lld",&g[i]);
	f[0]=1;
	cdq(0,n-1);
	for(int i=0; i<n; i++) printf("%lld ",f[i]);
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值