NTT(快速数论变换)多项式求逆 一千五百字解析

首先我们有\tiny F(x)G(x)\equiv 1(mod \,x^n)

我们对x^n进行向上取整开根号操作,即\tiny x^n\rightarrow x^{\left \lceil n/2 \right \rceil}

我们设\tiny F(x)H(x)\equiv 1(mod \, x^{\left \lceil \frac{n}{2} \right \rceil})

显然\tiny F(x)G(x)\equiv 1 (mod\, x^{\left \lceil \frac{n}{2} \right \rceil})

两式相减

\tiny F(x)(G(x)-H(x)) \equiv 0 (mod \, x^{\left \lceil \frac{n}{2} \right \rceil})

\tiny G(x)-H(x)\equiv 0 (mod \,x^{\left \lceil \frac{n}{2} \right \rceil })

所以减式构成的多项式在0-\tiny \left \lceil \frac{n}{2} \right \rceil-1次幂上都是0,否则他们本身就无法被\tiny x^{\left \lceil \frac{n}{2} \right \rceil}整除

根据这一发现

对减式进行平方

\tiny G(x)^2 +H(x)^2 -2G(x)H(x)\equiv 0 (mod \, x^n)

\tiny F(x)G(x)\equiv 1(mod \, x^n)

进行乘F(x)操作 再由

\tiny G(x)\equiv 2H(x)-F(x)H(x)^2 (mod \, x^n)

由此G(x)用NTT求解

# include<iostream>


using namespace std;
typedef long long int  ll;

const int mod=998244353,g=3,N=2000000;

int n;
ll a[N],b[N],c[N],rev[N];

ll qp(ll base,ll pow)
{
    ll ans=1;

    while(pow)
    {
        if(pow&1)
            ans=ans*base%mod;

        pow>>=1;

        base=base*base%mod;

    }

    return ans;

}

void init(int k)
{
    int len=(1<<k);

    for(int i=0;i<len;i++)
    {
        rev[i]=0;

    }

    for(int i=0;i<len;i++)
    {

        rev[i]=(rev[i>>1]>>1)|((i&1)<<(k-1));

    }
    return  ;

}

void NTT(ll *a,int n,int flag)
{

    for(int i=0;i<n;i++)
    {
        if(i<rev[i])
        {
            swap(a[i],a[rev[i]]);

        }
    }

    for(int h=1;h<n;h<<=1)
    {
        ll gn=qp(3ll,(mod-1)/(h*2));


        if(flag==-1)
            gn=qp(gn,mod-2);

        for(int j=0;j<n;j+=2*h)
        {
            ll gg=1;

            for(int k=j;k<j+h;k++)
            {
                ll x=a[k];

                ll y=gg*a[k+h]%mod;

                a[k]=(x+y)%mod;

                a[k+h]=((x-y)%mod+mod)%mod;

                gg=gg*gn%mod;


            }
        }
    }

    if(flag==-1)
    {
        ll inv=qp(n,mod-2);

        for(int i=0;i<n;i++)
        {
            a[i]=a[i]*inv%mod;

        }
    }
}
void work(int pow,ll*a,ll *b)
{
    if(pow==1)
    {
        b[0]=qp(a[0],mod-2);

        return ;

    }

    work((pow+1)>>1,a,b);

    int k=1,s=2;

    while((1<<k)<pow+pow-1)
    {
        k++;

        s<<=1;

    }

    init(k);

    for(int i=0;i<pow;i++)    
    {
        c[i]=a[i];
    }
   
 /*
之所以用c存a,而不对a进行直接操作是因为我们每次递归的
都必须是原数列的a
*/

    NTT(c,s,1);

    NTT(b,s,1);

    for(int i=0;i<s;i++)
    {
        b[i]=(ll)(2ll-c[i]*b[i]%mod+mod)%mod*b[i]%mod;
    }

    NTT(b,s,-1);

    for(int i=pow;i<s;i++)
    {
        b[i]=0;
    }

    return ;


}

int main ()
{

    cin>>n;

    for(int i=0;i<n;i++)
    {
        cin>>a[i];

    }

    work(n,a,b);

    for(int i=0;i<n;i++)
    {
        cout<<b[i]<<" ";

    }

    return 0;

}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

qinsanma and Code

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

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

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

打赏作者

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

抵扣说明:

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

余额充值