Processing math: 100%

多项式求逆元详解+模板 【洛谷P4238】多项式求逆

概述

多项式求逆元是一个非常重要的知识点,许多多项式操作都需要用到该算法,包括多项式取模,除法,开跟,求ln,求exp,快速幂。用快速傅里叶变换和倍增法可以在O(nlogn)的时间复杂度下求出一个n次多项式的逆元。

 

前置技能

快速数论变换(NTT),求一个数x在模p意义下的乘法逆元。

 

多项式的逆元

给定一个多项式A(x),其次数为degA,若存在一个多项式B(x),使其满足degBdegA,且A(x)×B(x)1(mod xn),则B(x)即为A(x)在模xn意义下的的乘法逆元。

 

求多项式的逆元

我们不妨假设,n=2k,kN

n=1,则A(x)×B(x)a0×b01(mod x1)。其中a0b0表示多项式A和多项式B的常数项。

若需要求出b0,直接用费马小定理求出a0的乘法逆元即可。

n>1时:

我们假设在模xn2的意义下A(x)的逆元B(x)我们已经求得。

依据定义,则有

A(x)B(x)1(mod xn2)          (1)

(1)式进行移项得

A(x)B(x)10(mod xn2)          (2)

然后对(2)式等号两边平方,得

A2(x)B2(x)2A(x)B(x)+10(mod xn)          (3)

将常数项移动到等式右侧,得

A2(x)B2(x)2A(x)B(x)1(mod xn)          (4)

将等式两边去相反数,得

2A(x)B(x)A2(x)B2(x)1(mod xn)          (5)

下面考虑回我们需要求的多项式B(x),依据定义,其满足

A(x)B(x)1(mod xn)(6)$

(5)(6)并移项,得

A(x)B(x)2A(x)B(x)A2(x)B2(x)(mod xn)          (7)

等式两边约去A(x),得

B(x)2B(x)A(x)B2(x)(mod xn)          (8)

 

 

显然,我们可以用上述式子求出B(x)

这一步的计算我们可以使用NTT,时间复杂度为O(nlogn)

我们可以通过递归的方法,求解出B(x)

时间复杂度T(n)=T(n2)+O(nlogn)=O(nlogn)

 

洛谷上有一道题目就叫做多项式求逆元(点这里),可以先做下那一题。

模板如下:

 

复制代码
 1 #include<bits/stdc++.h>
 2 #define M (1<<19)
 3 #define L long long
 4 #define MOD 998244353
 5 #define G 3
 6 using namespace std;
 7 
 8 L pow_mod(L x,L k){
 9     L ans=1;
10     while(k){
11         if(k&1) ans=ans*x%MOD;
12         x=x*x%MOD; k>>=1;
13     }
14     return ans;
15 }
16 
17 void change(L a[],int n){
18     for(int i=0,j=0;i<n-1;i++){
19         if(i<j) swap(a[i],a[j]);
20         int k=n>>1;
21         while(j>=k) j-=k,k>>=1;
22         j+=k;
23     }
24 }
25 void NTT(L a[],int n,int on){
26     change(a,n);
27     for(int h=2;h<=n;h<<=1){
28         L wn=pow_mod(G,(MOD-1)/h);
29         for(int j=0;j<n;j+=h){
30             L w=1;
31             for(int k=j;k<j+(h>>1);k++){
32                 L u=a[k],t=w*a[k+(h>>1)]%MOD;
33                 a[k]=(u+t)%MOD;
34                 a[k+(h>>1)]=(u-t+MOD)%MOD;
35                 w=w*wn%MOD;
36             }
37         }
38     }
39     if(on==-1){
40         L inv=pow_mod(n,MOD-2);
41         for(int i=0;i<n;i++) a[i]=a[i]*inv%MOD;
42         reverse(a+1,a+n);
43     }
44 }
45 
46 void getinv(L a[],L b[],int n){
47     if(n==1){b[0]=pow_mod(a[0],MOD-2); return;}
48     static L c[M],d[M];
49     memset(c,0,n<<4); memset(d,0,n<<4);
50     getinv(a,c,n>>1);
51     for(int i=0;i<n;i++) d[i]=a[i];
52     NTT(d,n<<1,1); NTT(c,n<<1,1);
53     for(int i=0;i<(n<<1);i++) b[i]=(2*c[i]-d[i]*c[i]%MOD*c[i]%MOD+MOD)%MOD;
54     NTT(b,n<<1,-1);
55     for(int i=0;i<n;i++) b[n+i]=0;
56 }
57 L a[M]={0},b[M]={0};
58 int main(){
59     int n,N; scanf("%d",&n);
60     for(int i=0;i<=n;i++) scanf("%lld",a+i);
61     for(N=1;N<=n;N<<=1);
62     getinv(a,b,N);
63     for(int i=0;i<=n;i++) printf("%lld ",b[i]);
64 }
复制代码

 

posted @   AlphaInf  阅读(3359)  评论(1编辑  收藏  举报
编辑推荐:
· 用 C# 插值字符串处理器写一个 sscanf
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· .NET Core内存结构体系(Windows环境)底层原理浅谈
· C# 深度学习:对抗生成网络(GAN)训练头像生成模型
阅读排行:
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 本地部署DeepSeek后,没有好看的交互界面怎么行!
· 趁着过年的时候手搓了一个低代码框架
· 用 C# 插值字符串处理器写一个 sscanf
· 推荐一个DeepSeek 大模型的免费 API 项目!兼容OpenAI接口!
点击右上角即可分享
微信分享提示