- 思路:
前置知识:分治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>