半在线卷积
半在线卷积,意为计算一个给定的多项式 \(f\) 与一个在线的多项式 \(g\) 卷积的结果。这里 在线的多项式 是指求 \(g_i\) 需要先求 \(g_{j<i}\)。模板题中的式子为 \(g_i=\sum_{j=1}^{i}f_jg_{i-j}\),有种自己与 \(f\) 卷积得到自己的感觉。
采用 cdq 分治的思路,如果要求 \(g(l,r)\) 先递归到左半边求出 \(g(l,mid)\),然后处理 \(g(l,mid)\) 对 \(g(mid+1,r)\) 的贡献。注意到这里的贡献是通过卷 \(f\) 的某一部分得到的,我们考虑 \(f\) 的哪些项会参与到 \([l,mid]\to[mid+1,r]\),显然是 \([1,r-l]\) 这一个前缀。因此我们可以把 \(g[l,mid]\) 与 \(f[1,r-l]\) 卷起来,取 \([mid+1,r]\) 这一段加到 \(g[mid+1,r]\) 上。时间复杂度 \(O(n\log^2n)\)
//author:望远星
#include<bits/stdc++.h>
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define mk make_pair
#define sml(x,y) (x=min(x,y))
#define big(x,y) (x=max(x,y))
//#define int ll
#define ll long long
#define ull unsigned long long
#define db double
#define fo(i,x,y) for(int i=x;i<=y;++i)
#define go(i,x,y) for(int i=x;i>=y;--i)
using namespace std;
inline int read(){int x=0,fh=1; char ch=getchar(); while(!isdigit(ch)){if(ch=='-') fh=-1; ch=getchar();} while(isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0'; ch=getchar();} return x*fh;}
inline void out(int *a,int l,int r){fo(i,l,r) cout<<*(a+i)<<' ';puts("");}
const int N=5e6,_g=3,qlr=998244353;
int a[N],b[N],n,rev[N],c[N],w[N],f[N],g[N],h[N],_n;
ull tmp[N];
inline int ksm(int x,int y){
int ans=1,t=x;
while(y){
if(y&1) ans=1ll*ans*t%qlr;
t=1ll*t*t%qlr;
y>>=1;
}
return ans;
}
void init(){
w[1]=1;int lim=1;while(lim<=2*n) lim<<=1;
for(int i=2;i<=lim;i<<=1){
int I=ksm(_g,(qlr-1)/i);
fo(j,0,i-1){
if(j&1) w[i+j]=1ll*w[i+j-1]*I%qlr;
else w[i+j]=w[(i>>1)+(j>>1)];
}
}
}
void fft(int *f,int n,int flg){
//cout<<"fft:n="<<n<<", ";out(f,0,n);
//if(flg)
// for(int i=2;i<=n;i<<=1) fo(j,1,i-1) w[i+j]=ksm(w[i+j],qlr-2);
fo(i,0,n-1) tmp[i]=f[rev[i]];
//fo(i,0,n-1) cout<<tmp[i]<<' ';puts("");
for(int len=2;len<=n;len<<=1){
if(len==(1<<17)) fo(i,0,n-1) tmp[i]%=qlr;
for(int i=0;i<n;i+=len)
fo(j,i,i+(len>>1)-1){
ull x=w[len+j-i]*tmp[j+(len>>1)]%qlr;
tmp[j+(len>>1)]=tmp[j]-x+qlr;
tmp[j]+=x;
}
}
fo(i,0,n-1) f[i]=tmp[i]%qlr;
if(flg){
int ni=ksm(n,qlr-2);
fo(i,0,n-1) f[i]=(ll)f[i]*ni%qlr;
reverse(f+1,f+n);
}
//out(f,0,n-1);
}
void solve(int l,int r){
if(l==r) return;
int mid=l+r>>1,n=mid-l,m=r-l;
solve(l,mid);
//printf("solve(%d,%d)\n",l,r);
//out()
fo(i,l,mid) f[i-l]=b[i];
fo(i,1,r-l) g[i]=a[i];
//别忘了重构 rev 数组!!
int lim=1;while(lim<=n+m) lim<<=1;
fo(i,0,lim) rev[i]=(rev[i>>1]>>1)|(i&1?lim>>1:0);
fo(i,mid-l+1,lim) f[i]=0;fo(i,r-l+1,lim) g[i]=0;
fft(f,lim,0);fft(g,lim,0);
fo(i,0,lim-1) h[i]=1ll*f[i]*g[i]%qlr;
fft(h,lim,1);
//out(h,0,lim-1);
fo(i,mid+1,r) b[i]=(b[i]+h[i-l])%qlr;
solve(mid+1,r);
}
signed main(){
//坑点:辅助数组用到的每个位置都要清零,包括 g[0]
//要重构 rev 数组,因为 lim 变了
//不要忘了递归到右半边
cin>>n;n--;init();_n=n;
fo(i,1,n) a[i]=read();
b[0]=1;fo(i,1,n) b[i]+=a[i];
solve(1,n);
out(b,0,n);
return 0;
}
/*
4
3 1 2
-------------------------------------------------
1 3 10 35
*/

浙公网安备 33010602011771号