P4721 【模板】分治 FFT
题意:
-
给定序列 g 1 … n − 1 g_{1\dots n - 1} g1…n−1,求序列 f 0 … n − 1 f_{0\dots n - 1} f0…n−1。
其中 f i = ∑ j = 1 i f i − j g j f_i=\sum_{j=1}^if_{i-j}g_j fi=∑j=1ifi−jgj ,边界为 f 0 = 1 f_0=1 f0=1
分析:
- 若直接按照题目给的式子展开求和, O ( n 2 ) O(n^2) O(n2) ,会超时
生成函数:
-
f i = ∑ j = 1 i f i − j g j f_i=\sum_{j=1}^if_{i-j}g_j fi=∑j=1ifi−jgj
设 F ( x ) = ∑ i = 0 ∞ f [ i ] x i F(x)=\sum_{i=0}^{\infty}f[i]x^i F(x)=∑i=0∞f[i]xi , G ( x ) = ∑ i = 0 ∞ g [ i ] x i G(x)=\sum_{i=0}^{\infty}g[i]x^i G(x)=∑i=0∞g[i]xi (设 g [ 0 ] = 0 g[0]=0 g[0]=0 )
F和G卷积:
- F ( x ) G ( X ) = ∑ i = 0 ∞ ∑ j = 0 ∞ f [ i ] g [ j ] x i + j F(x)G(X)=\sum_{i=0}^{\infty}\sum_{j=0}^{\infty}f[i]g[j]x^{i+j} F(x)G(X)=∑i=0∞∑j=0∞f[i]g[j]xi+j
令k=i+j
- F ( x ) G ( X ) = ∑ k = 0 ∞ ∑ j = 0 k f [ k − j ] g [ j ] x k F(x)G(X)=\sum_{k=0}^{\infty}\sum_{j=0}^{k}f[k-j]g[j]x^{k} F(x)G(X)=∑k=0∞∑j=0kf[k−j]g[j]xk
当k>0时, ∑ j = 0 k f [ k − j ] g [ j ] x k = f [ k ] \sum_{j=0}^{k}f[k-j]g[j]x^{k} = f[k] ∑j=0kf[k−j]g[j]xk=f[k]
当k=0时, ∑ j = 0 k f [ k − j ] g [ j ] x k = 0 ( g [ 0 ] = 0 ) \sum_{j=0}^{k}f[k-j]g[j]x^{k} = 0(g[0]=0) ∑j=0kf[k−j]g[j]xk=0(g[0]=0)
因此, F ( x ) G ( X ) = ∑ k = 1 ∞ f [ k ] x k F(x)G(X)=\sum_{k=1}^{\infty}f[k]x^{k} F(x)G(X)=∑k=1∞f[k]xk
所以, F ( x ) G ( X ) + f [ 0 ] = F ( x ) F(x)G(X)+f[0]=F(x) F(x)G(X)+f[0]=F(x)
然后移项:
- F ( x ) = f [ 0 ] 1 − G ( x ) = ( 1 − G ( x ) ) − 1 F(x)=\frac{f[0]}{1-G(x)}=(1-G(x))^{-1} F(x)=1−G(x)f[0]=(1−G(x))−1
题目就转换成了多项式求逆,套板子即可
注意:再将将 ( 1 − G ( x ) ) (1-G(x)) (1−G(x)) 看成整体,可表示为 , Q ( x ) = ( 1 − G ( x ) ) Q(x)=(1-G(x)) Q(x)=(1−G(x)) , g [ 0 ] = q [ 0 ] g[0]=q[0] g[0]=q[0] 初始为 1 1 1
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=(1<<20)+5, mo=998244353;
inline int ksm(int a,int b)
{
int ans=1;
while(b)
{
if(b&1) ans = ans*a%mo;
a = a*a%mo;
b >>= 1;
}
return ans;
}
int rev[N];
void ntt(int *a, int n, int inv)
{
for(int i=0;i<n;i++)
{
if(i < rev[i]) swap(a[i], a[rev[i]]);
}
for(int len=1;len<n;len<<=1)
{
int Wn = ksm(3, (mo-1)/(len<<1));
if(inv == -1) Wn = ksm(Wn, mo-2);
for(int i=0;i<n;i+=(len<< 1))
{
int w=1;
for(int j=0;j<len;j++, w = (w*Wn)%mo)
{
int x = a[i + j], y = w*a[i+j+len]%mo;
a[i+j] = (x+y)%mo; a[i+j+len] = (x-y+mo)%mo;
}
}
}
if(inv == -1)
{
int fg=ksm(n, mo-2);
for(int i=0;i<n;i++) a[i] = a[i]*fg%mo;
}
}
inline void mul(int *ta,int *tb,int *b,int k)
{
int s = 1<<k;
for(int i=1;i<s;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(k-1));
ntt(ta,s,1); ntt(tb,s,1);
for(int i=0;i<s;i++) b[i] = tb[i] * (2ll + mo - ta[i] * tb[i] % mo) % mo;
ntt(b,s,-1);
}
int ta[N], tb[N];
inline void polyinv(int *a, int *b, int len) //倍增多项式求逆
{
b[0] = ksm(a[0], mo-2);
for(int s=1, k=1; s<=len; s<<=1, k++)
{
int sum = s<<1;
for(int i=0;i<s;i++) ta[i] = a[i];
for(int i=0;i<s;i++) tb[i] = b[i];
mul(ta, tb, b, k);
for(int i=s;i<sum;i++) b[i] = 0;
}
}
int g[N], f[N];
signed main()
{
ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
int n;
cin>>n;
g[0] = 1;
for(int i=1;i<n;i++)
{
cin>>g[i];
g[i] = mo-g[i]; // 取减数
}
int s=2;
while(s < n) s <<= 1;
polyinv(g, f, s);
for(int i=0;i<n;i++) cout<<f[i]<<' ';
return 0;
}