模板题传送门
题目大意: 给出 g [ 1 ] , g [ 2 ] , . . . , g [ n − 1 ] g[1],g[2],...,g[n-1] g[1],g[2],...,g[n−1],求 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[n−1],其中 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[i−j]×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[r−l] (从 g [ r − l + 1 ] g[r-l+1] g[r−l+1] 开始,都不能使左区间对右区间产生贡献),于是我们将 g [ 1 ] g[1] g[1] ~ g [ r − l ] g[r-l] g[r−l] 也拎出来,放到 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拆分 题解