传送门
设
f
i
,
j
f_{i,j}
fi,j表示
f
i
(
x
)
f_i(x)
fi(x)的第
j
,
(
0
≤
j
≤
n
)
j,(0\le j\le n)
j,(0≤j≤n)项的系数,我们容易发现如下的转移关系:
f
i
,
j
⋅
j
b
i
+
1
→
f
i
+
1
,
j
−
1
f
i
,
j
⋅
c
i
+
1
→
f
i
+
1
,
j
f_{i,j}\cdot jb_{i+1}\rightarrow f_{i+1,j-1}\\f_{i,j}\cdot c_{i+1}\rightarrow f_{i+1,j}
fi,j⋅jbi+1→fi+1,j−1fi,j⋅ci+1→fi+1,j
箭头的意思是加过去的意思。
通过转移关系可以形成一条条路径,将一条路径上的边权的乘积叫做路径权值。
考虑
f
1
,
i
f_{1,i}
f1,i对
f
n
,
j
f_{n,j}
fn,j的贡献,容易发现贡献就是从
f
1
,
i
f_{1,i}
f1,i到
f
n
,
j
f_{n,j}
fn,j的所有路径的权值之和,注意到这条路径上我们恰好需要选择
i
−
j
i-j
i−j个
b
b
b项和
n
−
1
−
(
i
−
j
)
n-1-(i-j)
n−1−(i−j)个
c
c
c项,因此可以考虑
d
p
dp
dp,即设
d
p
l
,
r
[
k
]
dp_{l,r}[k]
dpl,r[k]表示
[
l
,
r
]
[l,r]
[l,r]区间内选择
k
k
k个
b
b
b项对应的乘之和,于是有
d
p
l
,
r
[
k
]
=
∑
i
+
j
=
k
d
p
l
,
m
i
d
[
i
]
⋅
d
p
m
i
d
+
1
,
r
[
j
]
dp_{l,r}[k]=\sum_{i+j=k}dp_{l,mid}[i]\cdot dp_{mid+1,r}[j]
dpl,r[k]=∑i+j=kdpl,mid[i]⋅dpmid+1,r[j],这是一个典型的卷积式,在递归回溯的时候用FFT合并即可,总复杂度
O
(
n
l
o
g
2
n
)
O(nlog^2n)
O(nlog2n)。
求出了 d p [ k ] dp[k] dp[k]数组后,那么 a n s [ j ] = ∑ i − j = k f 1 , i ⋅ d p [ k ] ⋅ i ! j ! ans[j]=\sum_{i-j=k}f_{1,i}\cdot dp[k]\cdot \frac{i!}{j!} ans[j]=∑i−j=kf1,i⋅dp[k]⋅j!i!,这也可以写成一个卷积式的形式,然后再做一次FFT就求出来 a n s ans ans数组了。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 400005;
const int mod = 998244353,g=3,gi=332748118;
typedef long long ll;
int qpow(int a,int b,int mod){
int ans=1;
while(b){
if(b&1)ans=1ll*ans*a%mod;
b>>=1;
a=1ll*a*a%mod;
}
return ans;
}
int a[maxn],b[maxn],c[maxn],pos[maxn],fav[maxn],fac[maxn],dp[maxn],aux1[maxn],aux2[maxn],h[maxn];
int as[maxn];
void ntt(int *a,int n,int fg){
if(n==1)return;
for(int i=0;i<n;++i)if(i<pos[i])swap(a[i],a[pos[i]]);
for(register int len=2,mid=1;len<=n;len*=2,mid*=2){
int wn=qpow(fg==1?g:gi,(mod-1)/len,mod);
for(register int i=0;i<n;i+=len){
int w=1;
for(int j=0;j<mid;++j){
ll c=a[i+j],d=1ll*w*a[i+j+mid]%mod;
a[i+j]=(c+d)%mod;
a[i+j+mid]=(c-d+mod)%mod;
w=1ll*w*wn%mod;
}
}
}
int invn=qpow(n,mod-2,mod);
if(fg==-1)for(int i=0;i<n;++i)a[i]=1ll*a[i]*invn%mod;
}
void pre(int n)
{
fac[0]=1;
for(int i =1;i<=n;++i){
fac[i]=1ll*fac[i-1]*i%mod;
}
fav[n]=qpow(fac[n],mod-2,mod);
for(int i =n-1;i>=0;i--){
fav[i]=1ll*fav[i+1]*(i+1)%mod;
}
}
int init(int n,int m)
{
int len=1,bt=0;
while(len<n+m-1)len*=2,bt++;
for(int i=0;i<len;++i)pos[i]=(pos[i>>1]>>1)|((i&1)<<(bt-1));
return len;
}
/*
vector<int> ntt1(const vector<int>&a,const vector<int>&b)
{
int n=a.size(),m=b.size();
vector<int>ans(n+m-1);
if(n<=50 && m<=50){
for(int i=0;i<n;++i){
for(int j=0;j<m;++j){
ans[i+j]=(ans[i+j]+1ll*a[i]*b[j]%mod)%mod;
}
}
return ans;
}
for(int i=0;i<n;++i)aux1[i]=a[i];
for(int i=0;i<m;++i)aux2[i]=b[i];
int len=init(n,m);
ntt(aux1,len,1);
ntt(aux2,len,1);
for(int i=0;i<len;++i)aux1[i]=1ll*a[i]*b[i]%mod;
ntt(aux1,len,-1);
for(int i=0;i<ans.size();++i)ans[i]=aux1[i];
for(int i=0;i<len;++i)aux1[i]=aux2[i]=0;
return ans;
}*/
void work(int l,int r)
{
if(l==r){
as[2*l]=c[l];
as[2*l+1]=b[l];
return;
}
int mid=l+r>>1;
work(l,mid),work(mid+1,r);
int n=0,m=0;
for(int i=2*l,cnt=mid-l+2;cnt;i++,cnt--)aux1[n++]=as[i];
for(int i=2*(mid+1),cnt=r-mid+1;cnt;i++,cnt--)aux2[m++]=as[i];
int len=init(n,m);
ntt(aux1,len,1);
ntt(aux2,len,1);
for(int i=0;i<len;++i)aux1[i]=1ll*aux1[i]*aux2[i]%mod;
ntt(aux1,len,-1);
for(int i=0;i<=r-l+1;++i)as[2*l+i]=aux1[i];
for(int i =0;i<len;++i)aux1[i]=aux2[i]=0;
return;
}
int main(){
pre(maxn-1);
int t;
scanf("%d",&t);
while(t--){
int n;
scanf("%d",&n);
for(int i= 0;i<=n;++i)scanf("%d",&a[i]);
for(int i =0;i<n-1;++i)scanf("%d",&b[i]);
for(int i=0;i<n-1;++i)scanf("%d",&c[i]);
work(0,n-2);
int len=init(n+1,n+1);
for(int i=0;i<=n-1;++i)dp[i]=as[i];dp[n]=0;
for(int i =0;i<=n;++i)h[i]=1ll*a[n-i]*fac[n-i]%mod;
ntt(dp,len,1);
ntt(h,len,1);
for(int i =0;i<len;++i)dp[i]=1ll*dp[i]*h[i]%mod;
ntt(dp,len,-1);
for(int i =n;i>=0;--i){
printf("%lld",1ll*dp[i]*fav[n-i]%mod);
if(i)putchar(' ');
}
for(int i=0;i<len;++i)dp[i]=h[i]=0;
puts("");
}
return 0;
}