P4238 【模板】多项式乘法逆


原题链接

P4238
AC记录:Accepted

题目大意

给定一个多项式 F ( x ) F(x) F(x),请求出一个多项式 G ( x ) G(x) G(x),满足 F ( x ) ⋅ G ( x ) ≡ 1 ( m o d x n ) F(x)\cdot G(x)\equiv 1\pmod{x^n} F(x)G(x)1(modxn)。系数对 998244353 998244353 998244353 取模。

输入格式

第一行一个整数 n n n
接下来一行 n n n 个数字,从低到高表示 F ( x ) F(x) F(x) 的系数。

输出格式

一行 n n n 个数字,从低到高表示 G ( x ) G(x) G(x) 的系数,保证有解。

S a m p l e \mathbf{Sample} Sample I n p u t \mathbf{Input} Input

5
1 6 3 4 9

S a m p l e \mathbf{Sample} Sample O u t p u t \mathbf{Output} Output

1 998244347 33 998244169 1020

H i n t & E x p l a i n \mathbf{Hint\&Explain} Hint&Explain
图像画不出来。(实际上不想做)

数据范围

对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 1 0 5 , 0 ≤ a i ≤ 1 0 9 1 \le n\le 10^5,0\le a_i \le 10^9 1n105,0ai109

解题思路

在介绍本题之前,你需要知道一些前置的知识。


前置知识

1.NTT \texttt{1.NTT} 1.NTT
不会NTT的同学可以看我写的关于NTT的详解。
Click   me! \texttt{Click me!} Click me!

正文部分

首先,我们知道
F ( x ) ⋅ G ( x ) ≡ 1 ( m o d x n ) F(x)\cdot G(x)\equiv 1\pmod{x^n} F(x)G(x)1(modxn)
假设我们现在已经求出了 F ( x ) F(x) F(x) 在模 x ⌈ n 2 ⌉ x^{\left\lceil\frac n2\right\rceil} x2n 下的逆元 B B B,则有
F ( x ) ⋅ B ( x ) ≡ 1 ( m o d x ⌈ n 2 ⌉ ) F(x)\cdot B(x)\equiv 1\pmod{x^{\left\lceil\frac n2\right\rceil}} F(x)B(x)1(modx2n)
两式相减,再同时除以 F ( x ) F(x) F(x),得
G ( x ) − B ( x ) ≡ 0 ( m o d x ⌈ n 2 ⌉ ) G(x)-B(x)\equiv0\pmod{x^{\left\lceil\frac n2\right\rceil}} G(x)B(x)0(modx2n)
平方,得
G ( x ) 2 − 2 G ( x ) B ( x ) + B ( x ) 2 ≡ 0 ( m o d x n ) G(x)^2-2G(x)B(x)+B(x)^2\equiv0\pmod{x^n} G(x)22G(x)B(x)+B(x)20(modxn)
乘上 F ( x ) F(x) F(x),得
G ( x ) − 2 B ( x ) + F ( x ) B ( x ) 2 ≡ 0 ( m o d x n ) G(x)-2B(x)+F(x)B(x)^2\equiv0\pmod{x^n} G(x)2B(x)+F(x)B(x)20(modxn)
移项,得
G ( x ) = 2 B ( x ) − F ( x ) B ( x ) 2 ( m o d x n ) G(x)=2B(x)-F(x)B(x)^2\pmod{x^n} G(x)=2B(x)F(x)B(x)2(modxn)
B ( x ) B(x) B(x)提取出来,得到
G ( x ) = ( 2 − F ( x ) ⋅ B ( x ) ) ⋅ B ( x ) ( m o d x n ) G(x)=(2-F(x)\cdot B(x))\cdot B(x)\pmod{x^n} G(x)=(2F(x)B(x))B(x)(modxn)
直接从小到大递归用多项式乘法套公式即可。

上代码

#include<bits/stdc++.h>

#define endl '\n'

using namespace std;

#define int ll

typedef long long ll;

namespace Polynomial{
    // The value of the variable below can be changed.
    const int   max_size_of_array=400000;
    // The value of the variable above can be changed.
    const int   maxSize=max_size_of_array+10;
    const int   g=3,invg=332748118;
    const int   p=998244353;
    int         resort[maxSize];
    int         inv1[maxSize];
    int         lim,dig;

    void print_elem(string name,int *f,int len)
    {
        cout<<name<<":";
        for(int i=0; i<len; i++)
            cout<<f[i]<<" ";
        cout<<endl;
        return;
    }

    int power(int a,int b,int p)
    {
        int tar=1;
        for(; b; b>>=1,a=(1ll*a*a)%p)
            if(b&1)
                tar=(1ll*tar*a)%p;
        return tar%p;
    }

    void NTT(int *c,int lim,int state)
    {
        // cout<<"NTT"<<endl;
        for(int i=0; i<lim; i++)
            if(i<resort[i])
                swap(c[i],c[resort[i]]);
        for(int i=1; i<lim; i<<=1)
        {
            int W1n=power(g,1ll*(p-1)/(i<<1),p);
            for(int Size=i<<1,j=0; j<lim; j+=Size)
            {
                int W=1;
                for(int k=0; k<i; k++,W=(1ll*W*W1n)%p)
                {
                    int x=(1ll*c[j+k]+p)%p,y=(1ll*W*c[j+i+k]+p)%p;
                    c[j+k]=(1ll*x+y+p)%p;
                    c[j+i+k]=(1ll*x-y+p)%p;
                    // if(j+k==0)
                        // cout<<"\t"<<c[j+k]<<"\tNTT"<<endl;
                }
            }
        }
        if(state==1)
            return;
        int temp=power(lim,p-2,p);
        reverse(c+1,c+lim);
        for(int i=0; i<lim; i++)
            c[i]=1ll*c[i]*temp%p;
        return;
    }

    void getInv(int *f,int *g,int size)
    {
        // cout<<"getInv "<<size<<" "<<((size+1)>>1)<<endl;
        // getInv(f,g,size);
        if(size==1)
        {
            g[0]=power(f[0],p-2,p);
            // print_elem("\t",g,size);
            return;
        }
        // print_elem("\t",g,size);
        getInv(f,g,(size+1)>>1);
        // print_elem("\t",g,size);
        lim=1,dig=0;
        while(lim<(size<<1))
            lim<<=1,dig++;
        for(int i=1; i<lim; i++)
            resort[i]=(resort[i>>1]>>1)|((i&1)<<(dig-1));
        for(int i=0; i<size; i++)
            inv1[i]=f[i];
        for(int i=size; i<lim; i++)
            inv1[i]=0;
        NTT(inv1,lim,1);
        NTT(g,lim,1);
        for(int i=0; i<lim; i++)
            g[i]=1ll*((2ll-inv1[i]*g[i]%p+p)%p*g[i]%p+p)%p;
        NTT(g,lim,-1);
        for(int i=size; i<lim; i++)
            g[i]=0;
        // print_elem("\t",g,size);
        return;
    }
}

using namespace Polynomial;

int         a[maxSize];
int         b[maxSize];
int         n;

signed main()
{
    // ios::sync_with_stdio(false);
    cin.tie(0);
    /* Code */
    cin>>n;
    for(int i=0; i<n; i++)
        cin>>a[i];
    getInv(a,b,n);
    for(int i=0; i<n; i++)
        cout<<b[i]<<" ";
    cout<<endl;
    return 0;
}

完美切题 ∼ \sim

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值