关于逆元的总结

一.逆元的定义
ab ≡ \equiv 1 mod p,则称b为a模p意义下的逆元。
逆元可以看作是一个分子为一的分数。
二.逆元的应用
逆元主要用于用于求组合数。
三.求逆元的方法

1.费马小定理求逆元
根据费马小定理a^(p-1)%p=1%p
等式两边同除以a得,a^(p-2)%p=(1/a)%p
得a在模p意义下的逆元就等于a^(p-2)%p,用快速幂,可在log2§的复杂度中求出。

#include<bits/stdc++.h>
using namespace std;
int qsm(int x,int y,int mod){//快速幂
    int res=1,k=x;
    while(y){
       if(y&1) res=(res*k)%mod;
       k=(k*k)%mod;
       y>>=1;
    }
    return res%mod;
}
int main(){
    int n,mod;
    scanf("%d%d",&n,&mod);
    for(int i=1;i<=n;i++){
        ans=qsm(i,mod-2,mod);
        printf("%d在模mod意义下的逆元为%d\n",i,ans);
    }
    return 0;
}
时间复杂度为O(n*log2(mod)

2.预处理求逆元
推导过程如下:
推导过程

代码:

#include<bits/stdc++.h>//求1~n在模p意义下的逆元
using namespace std;
const int N=3e6+10;
int n;
long long p,inv[N];
int main(){
	scanf("%d%d",&n,&p);
	inv[1]=1;//1在模p意义下的逆元为1
	for(int i=1;i<=n;i++){
		if(i!=1) inv[i]=(p-(p/i))*inv[p%i]%p;//p%i一定小于i,则inv[p%i]已在之前计算过
		printf("%d\n",inv[i]);
	}
	return 0;
}
时间复杂度为O(n)

3.通过求阶乘的逆元求每个数的逆元
过程为:
1.递推求出1~n阶乘数组fac[]
2.用费马小定理求出fac[n]的逆元g[n]
3.根据g[i]=g[i+1]*(i+1) 递推出每个g[i]
4.则数x的逆元为g[x]*fac[x-1]
这种算法复杂度也为O(n),但相较于第二种算法的优势是处理了阶乘数组,和阶乘逆元数组。
在计算组合数时可直接调用。

代码如下:

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int T,n,m;
long long fac[N],g[N],h[N];
int q_pow(int x,int y,int m){
	long long res=1,k=x;
	while(y){
		if(y&1) res=(res*k)%m;
		k=(k*k)%m;
		y>>=1;
	}
	return res%m;
}
void Get_inv(){
	fac[0]=1;
	for(int i=1;i<N;i++)
	    fac[i]=(fac[i-1]*i)%m;
	g[N-1]=q_pow(fac[N-1],m-2,m);  
	for(int i=N-2;i>=0;i--){
		g[i]=(g[i+1]*(i+1))%m;
		h[i]=(g[i]*fac[i-1])%m;		
	}

}
int main(){
	scanf("%d%d",&n,&m);
	Get_inv();
	for(int i=1;i<=n;i++){
		printf("%lld\n",h[i]);
	}
	return 0;
}
时间复杂度为O(n)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值