• 思路:

前置知识:分治fft(会求下降幂),多项式带余除法

首先能想到的一种做法是,多项式快速求值,然后IFDT一下。但多项式快速求值我都用分治fft……所以也可以用下面这种:

虽然是\(O(nlog^2n)\)但常数小,而且好些。注意一下节点开动态空间不要越界,而且要注意一下各种上界
\(F(x)=\sum\limits_{i=0}^{n-1}F[i]x^{\underline{i}} = \sum\limits_{i=0}^{mid} F[i]x^{\underline{i}} +x^{\underline{mid+1}} \sum\limits_{mid+1}^{n-1}F[i](x-mid-1)^{\underline{i-mid-1}}\)

要知道\(F\)每一项系数,除以\(x^{\underline{mid+1}}\),得到余数(前一半),商(后一半),分治下去,直到长度为1输出结果。

这之前还要跑一次分治fft求下降幂,且像线段树一样每个节点开动态空间(类似vector)记录当前区间下降幂答案。
合并就两个区间乘起来即可。

  • code
点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef double db;
typedef long long ll;

const int N=1e6+5;
const ll mod=998244353;
inline ll ksm(ll a,ll b) {ll mul=1;for(;b;b>>=1,a=a*a%mod)if(b&1)mul=mul*a%mod;return mul;}
const ll I=ksm(3,(mod-1)/4);
const ll inv2=ksm(2,mod-2);

ll inv[N],inv3,gen[2][N];
int rev[N],L,up;
void NTT(ll *a,int op) {
	for(int i=0;i<up;i++) {
		if(rev[i]>i)swap(a[i],a[rev[i]]);
	}
	for(int mid=1;mid<up;mid<<=1) {
		int len=mid<<1;ll w1=gen[op][len];
 		for(int l=0;l<up;l+=len) {
 			ll W=1;
			for(int i=0;i<mid;i++,W=W*w1%mod) {
				int p=l+i,q=p+mid;
				ll x=a[p],y=W*a[q];
				a[p]=(x+y)%mod;a[q]=(x-y)%mod;
			}
		}
	}
}

void gt_up(int len) {
	up=1,L=0;
	while(up<=len) {up<<=1,L++;}
	for(int i=1;i<up;i++) {rev[i]=(rev[i>>1]>>1)|((i&1)<<(L-1));}
}

ll a[N];
void poly_inv(int dep,ll *f,ll *b) {	// b = f mod(x^dep) 
	if(dep==1) {b[0]=ksm(f[0],mod-2);return;}
	poly_inv((dep+1)>>1,f,b);
	gt_up(dep<<1);
	for(int i=0;i<dep;i++)a[i]=f[i];for(int i=dep;i<up;i++)a[i]=b[i]=0;
	NTT(b,0);NTT(a,0);
	for(int i=0;i<up;i++) {a[i]=(2-a[i]*b[i]%mod)*b[i]%mod;}
	NTT(a,1);
	ll i_up=ksm(up,mod-2);
	for(int i=0;i<dep;i++) {b[i]=a[i]*i_up%mod;} for(int i=dep;i<up;i++)b[i]=0;
}

void gt_gen(int len) {
	inv3=ksm(3,mod-2);
	gt_up(len);
	gen[0][up]=ksm(3,(mod-1)/up);gen[1][up]=ksm(inv3,(mod-1)/up);
	for(int i=up;i;i>>=1) {
		gen[0][i>>1]=gen[0][i]*gen[0][i]%mod;
		gen[1][i>>1]=gen[1][i]*gen[1][i]%mod;
	}
	inv[1]=1;for(int i=2;i<up;i++)inv[i]=(mod-mod/i)*inv[mod%i]%mod;
}

ll iG[N],Gr[N],Fr[N],Qr[N];
void poly_div(int n,int m,ll *F,ll *G,ll *Q,ll *R) {
	int q=n-m+1;
	if(q<=0) {Q[0]=0;for(int i=0;i<m-1;i++)R[i]=(i<n)?F[i]:0;return;}
	for(int i=0;i<n;i++)Fr[n-1-i]=F[i];
	for(int i=0;i<m;i++)Gr[m-1-i]=G[i];
	//get Q (mod q)
	for(int i=0;i<q;i++)iG[i]=0;
	poly_inv(q,Gr,iG);
	gt_up(q<<1);ll tmp=ksm(up,mod-2);
	for(int i=q;i<up;i++)iG[i]=Fr[i]=0;
	NTT(Fr,1),NTT(iG,1);
	for(int i=0;i<up;i++)Qr[i]=iG[i]*Fr[i]%mod;
	NTT(Qr,0);
	for(int i=0;i<q;i++) {
		R[i]=Q[i]=Qr[q-1-i]*tmp%mod;
	}
	//get R
	gt_up(n);tmp=ksm(up,mod-2);
	for(int i=0;i<up;i++)iG[i]=(i<m)?G[i]:0;
	for(int i=q;i<up;i++)R[i]=0;
	NTT(iG,1);NTT(R,1);	//Q will return by ans so used R instead of Q
	for(int i=0;i<up;i++)R[i]=R[i]*iG[i]%mod;
	NTT(R,0);
	for(int i=0;i<m-1;i++) {
		R[i]=(F[i]-R[i]*tmp)%mod;
	}
	for(int i=m-1;i<up;i++)R[i]=0;
}

ll *A[N],tx[N],ty[N];
int len[N];
void solve1(int o,int l,int r) {	//倍增下降幂 
	A[o]=new ll[len[o]=r-l+2];
	if(l==r) {A[o][0]=1-l;A[o][1]=1;return;}
	int mid=(l+r)>>1,ls=o<<1,rs=ls|1;
	solve1(ls,l,mid),solve1(rs,mid+1,r);
	gt_up(len[o]);
	for(int i=0;i<up;i++) {
		tx[i]=(i<len[ls])?A[ls][i]:0;
		ty[i]=(i<len[rs])?A[rs][i]:0;
	}
	NTT(tx,1),NTT(ty,1);
	for(int i=0;i<up;i++)tx[i]=tx[i]*ty[i]%mod;
	NTT(tx,0);
	for(int i=0,j=ksm(up,mod-2);i<len[o];i++) {A[o][i]=tx[i]*j%mod;}
}

void solve2(int o,int l,int r,ll *f) {
	if(l==r) {printf("%lld ",(f[0]+mod)%mod);return;}
	int mid=(l+r)>>1,ls=o<<1,rs=ls|1;
	ll tl[len[o]<<1],tr[len[o]<<1];
	poly_div(len[o]-1,len[ls],f,A[ls],tr,tl);
	solve2(ls,l,mid,tl),solve2(rs,mid+1,r,tr);
}

ll F[N];
int main() {
	int n;scanf("%d",&n);
	gt_gen(n+1);
	for(int i=0;i<n;i++) {scanf("%lld",&F[i]);}
	solve1(1,1,n);
	solve2(1,1,n,F);
	return 0;
}

</details>