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

Definition

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

x \times y \equiv 1 (mod \quad p)

则称 y 是 x 在模 p 意义下的逆元,记做 x^{-1}~\equiv y \pmod px−1 ≡y(modp)

一个数字逆元在模意义下的运算中可以完全取代该数字的倒数。例如 x^{-1}~\equiv y \pmod px−1 ≡ y(modp),其中 y^{-1} 代表 y的逆元。

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

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

具体如下:

根据欧拉定理

x^{\phi(p)} \equiv 1 \pmod pxϕ(p)≡1(modp)

其中 \phiϕ为欧拉函数,\phi(p )ϕ(p) 表示小于 p 的正整数中与 p 互质的数的个数。

等式两侧同乘 x^{-1} 可以得到

x^{\phi(p) - 1} \equiv x^{-1} \pmod pxϕ(p)−1≡x−1 (modp)

显然当 p 是一个质数时,\phi(p) = p - 1 ϕ(p)=p−1,这时可以 O(1) 算出\phi(p) - 1 = p-2 的值,即可用快速幂  求出 x的逆元。这个算法好写好记,常数也较小。一般当 p 为 int 范围内的质数时选择此算法。当 p 不在 int 范围内时,由于快速幂时需要两个 long long 相乘,会爆精度。

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

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

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

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

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

最一般的,给你n个数:

我们设数组a的各项是:a_1,a_2,a_3......a_n

则其逆元可表示为:\frac{1}{a_1},\frac{1}{a_2},\frac{1}{a_3},......\frac{1}{a_n}

\frac{1}{a_i}=pre[i-1]*suf[i+1]*inv(\prod_{1}^{n} a_i)

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

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

附上两题模板题:

P3811 【模板】乘法逆元

#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

#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;
    
}
发布了257 篇原创文章 · 获赞 141 · 访问量 21万+

猜你喜欢

转载自blog.csdn.net/weixin_43798170/article/details/104886329
今日推荐