P4721 【模板】分治 FFT (多项式求逆板子,生成函数)

53 篇文章 0 订阅
44 篇文章 0 订阅

P4721 【模板】分治 FFT

题意:
  • 给定序列 g 1 … n − 1 g_{1\dots n - 1} g1n1,求序列 f 0 … n − 1 f_{0\dots n - 1} f0n1

    其中 f i = ∑ j = 1 i f i − j g j f_i=\sum_{j=1}^if_{i-j}g_j fi=j=1ifijgj ,边界为 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=1ifijgj

    F ( x ) = ∑ i = 0 ∞ f [ i ] x i F(x)=\sum_{i=0}^{\infty}f[i]x^i F(x)=i=0f[i]xi , G ( x ) = ∑ i = 0 ∞ g [ i ] x i G(x)=\sum_{i=0}^{\infty}g[i]x^i G(x)=i=0g[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=0j=0f[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=0j=0kf[kj]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[kj]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[kj]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=1f[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)=1G(x)f[0]=(1G(x))1

    题目就转换成了多项式求逆,套板子即可

    注意:再将将 ( 1 − G ( x ) ) (1-G(x)) (1G(x)) 看成整体,可表示为 , Q ( x ) = ( 1 − G ( x ) ) Q(x)=(1-G(x)) Q(x)=(1G(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;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yezzz.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值