前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >乘法逆元 线性递推阶乘求逆元、费马小定理、普适线性求逆元 欧拉定理结论

乘法逆元 线性递推阶乘求逆元、费马小定理、普适线性求逆元 欧拉定理结论

作者头像
glm233
发布2020-09-29 23:41:15
1K0
发布2020-09-29 23:41:15
举报

Definition

对于一个数 x 和一个模数 p,若存在一个数字 y,满足

则称 y 是 x 在模 p 意义下的逆元,记做

一个数字逆元在模意义下的运算中可以完全取代该数字的倒数。例如

,其中

代表 y的逆元。

单个数求逆元有扩展欧几里得、费马小定理 复杂度logn

对于竞赛中,一般常用的是费马小定理,因为模数一般是质数

具体如下:

根据欧拉定理

其中

为欧拉函数,

表示小于 p 的正整数中与 p 互质的数的个数。

等式两侧同乘

可以得到

显然当 p 是一个质数时,

,这时可以 O(1) 算出

的值,即可用快速幂 求出 x的逆元。这个算法好写好记,常数也较小。一般当 p 为 int 范围内的质数时选择此算法。当 p 不在 int 范围内时,由于快速幂时需要两个 long long 相乘,会爆精度。

至于证明,自行百度,会用就行~

线性递推阶乘求逆元结论:

其中p为模数,inv[i]表示数i的逆元

对了,欧拉定理有个好用的结论,需要记下来:

通过这个式子可以有效的简化模数

最一般的,给你n个数:

我们设数组a的各项是:

则其逆元可表示为:

其中pre[i]表示前i个数相乘,suf[i]表示从第n个数开始倒回去乘到第i个数,(模p意义下)

这样o(n)我们就可以求出这些数的逆元了~

附上两题模板题:

P3811 【模板】乘法逆元

代码语言:javascript
复制
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define rg register ll
#define inf 2147483647
#define lb(x) (x&(-x))
ll sz[200005],n;
template <typename T> inline void read(T& x)
{
    x=0;char ch=getchar();ll f=1;
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}x*=f;
}
inline ll query(ll x){ll res=0;while(x){res+=sz[x];x-=lb(x);}return res;}
inline void add(ll x,ll val){while(x<=n){sz[x]+=val;x+=lb(x);}}//第x个加上val
ll p,inv[5000005]={0,1};
int main()
{
	
	cin>>n>>p;
	cout<<1<<endl;
	for(rg i=2;i<=n;i++)
	{
		inv[i]=p-(p/i)*inv[p%i]%p;
		printf("%lld\n",inv[i]);
	}
    return 0;
    
}

P5431 【模板】乘法逆元2

代码语言:javascript
复制
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define rg register ll
#define inf 2147483647
#define lb(x) (x&(-x))
template <typename T> inline void read(T& x)
{
    x=0;char ch=getchar();ll f=1;
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}x*=f;
}
ll a[5000005],sum,suf[5000005],n,p,k,pre;
inline ll qp(ll x,ll m)
{
	ll res=1;
	while(m)
	{
		if(m&1)res=res*x%p;
		x=x*x%p;
		m>>=1;
	}
	return res%p;
}
inline ll in(ll x)
{
	return qp(x,p-2);
}
int main()
{
	cin>>n>>p>>k;
	ll k1=k;
	pre=1,suf[n+1]=1;
	for(rg i=1;i<=n;i++)read(a[i]);
	for(rg i=n;i>=1;i--)suf[i]=suf[i+1]*a[i]%p;
	ll zong=in(suf[1]);
	for(rg i=1;i<=n;i++)sum+=zong%p*pre%p*suf[i+1]%p*k1,sum%=p,k1=k1*k%p,pre=pre*a[i]%p;
	cout<<sum<<endl;
    return 0;
    
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-03-15 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Definition
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档