逆元

逆元

逆元定义:若\(a*x=1(\mod b)\)\(a,b\)互质,则称\(x\)\(a\)的逆元,记作\(a^{-1}\)

逆元应用

\((t/a)\mod b\) 时,转化为\(t*a^{-1} \mod b\)

三种方法求逆元

此题为例

扩展欧几里德定理

根据定义可转化为\(a*x+b*y=1\),满足扩展欧几里德求解条件

求出\(x\)的最小整数解\(t=b/gcd(a,b),x=(x\mod t+t)\mod t\)扩欧讲解

此题中默认 \(gcd(a,b)=1\)

所以最小整数解\(x=(x\mod b+b)\mod b=(x+b)\mod b\)

  • code
typedef long long ll;
ll exgcd(ll a,ll b,ll &x,ll &y){
	if(!b) {
		x=1,y=0;
		return a;
	}
	ll temp=x;
	x=y;
	y=temp-(a/b)*y;
} 
ll ex_inv(ll a,ll b){
	ll d,x,y;
	d=exgcd(a,b,x,y);
	return d==1?(x+b)%b:-1;//互质才有解 
}

快速幂

\(a*x=1(\mod p)\)

又因为\(a^{p-1}=1(\mod p)\)

所以\(a^{p-1}=a*x(\mod p)\)(传递性)

又利用同乘性,两边同乘\(a^{-1}\)

\(a^{p-2}=x(\mod p)\)

则可以用快速幂求出\(a^{p-2}\)

  • code
typedef long long ll;
ll quickpow(ll a,ll b,ll mod){
	ll temp=a,ans=1;
	while(b){
		if(b&1) ans=(ans*temp)%mod;
		temp=(temp*temp)%mod;
		b>>=1;
	}return ans%mod;
}
ll inv(ll a,ll b){
	return quickpow(a,b-2,b); //要求b一定为质数,因为费马定理要求指数为b为素数 
}

线性求法

易得\(1^{-1}=1(\mod p)\)

\(p=k*i+r(r<i,1<i<p)k=p/i,r=p \mod i\) 式子转化为同余式,\(k*i+r==0(\mod p)\)
(注意根据i的范围,我们只能求小于p的数的逆元)
利用同乘性,两边同乘\(i^{-1}\)\(,r^{-1}\)

则转化为

\(k*r^{-1}+i^{-1}=0 (\mod p)\)

\(i^{-1}=-k*r^{-1} (\mod p)\)

\(i^{-1}=-\frac{p}{i}+(p \mod i)^{-1} (\mod p)\)

而逆元也是正数,所以为了防止出现负数可以变为(p-frac{p}{i}+(p \mod i)^{-1}) (\mod p) (因为p \mod p=0,所以可以直接加p)$

int inv[maxn];
inv[1]=1;
for(int i=2;i<=n;++i) 
	inv[i]=(p-p/i)*inv[p%i]%p;

根据递推式,所以我们可以写一个递归求解逆元

逆元模板2

推一下,通个分

\(\sum_{i=1}^{n}\frac{k^i}{a_i}=\sum_{i=1}^{n}\frac{k^i*\frac{s}{a_i}}{s}\)

其中\(s=\prod_{i=1}^{n}a_i\)

那么\(\frac{s}{a_i}=\prod_{j=1}^{i-1}a_j * \prod_{j=i+1}^{n}\)

一个前缀积和后缀积就可以实现

分子全部加在一起

直接求分母s的逆元即可(同指数,底数可以相加)

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
using namespace std;

long long n,p,k;
const int maxn=5000000+5;
long long a[maxn],ans=0,pre[maxn],nt[maxn];
inline long long read(){
	long long x=0,f=1;
	char ch=getchar();
	while(ch>'9' || ch<'0'){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}return x*f;
}
inline int inv(int x){//递归推逆元
	if(x==1)return 1;
	return (long long)((p-p/x)*inv(p%x)%p) ;
}
int main(){	
	n=read(),p=read(),k=read();
	for(int i=1;i<=n;++i)	a[i]=read();
	pre[0]=nt[n+1]=1;
	for(register int i=1;i<=n;++i) pre[i]=(long long)(pre[i-1]*a[i])%p; //前缀积
	for(register int i=n;i>=1;--i) nt[i]=(long long)(nt[i+1]*a[i])%p;//后缀积
	long long temp=k;
	for(register int i=1;i<=n;++i){
		ans=(ans+((nt[i+1]*pre[i-1])%p)*temp)%p;求//分子
		temp=(temp*k)%p;//相对应的k
	}
	printf("%lld\n",ans*(long long)inv(pre[n])%p);
	return 0;
} 
posted @ 2021-08-23 16:52  归游  阅读(145)  评论(0编辑  收藏  举报