参考:http://blog.csdn.net/yukizzz/article/details/51105009
在求解除法取模问题(a/b)%n时,我们可以转化为(a%(b∗n))/b,
但是如果b很大,则会出现爆精度问题,所以我们避免使用除法直接计算。
可以使用逆元将除法转换为乘法:
假设b存在乘法逆元,即与m互质(充要条件)。设c是b的逆元,即b∗c≡1(mod n),那么有a/b=(a/b)∗1=(a/b)∗b∗c=a∗c(mod n)
即,除以一个数取模等于乘以这个数的逆元取模。
下面就HDU1576 A/B这道题来说三种方法的实现
1. 逆元求解一般利用扩欧。
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long LL;
const int MD = 9973;
LL exgcd(LL a,LL b,LL &x,LL &y){
if(b==0){
x = 1;y =0; return a;
}
else{
LL d = exgcd(b,a%b,x,y);
LL t = x; x = y; y = t - a/b * y;
return d;
}
}
LL inv(LL b,LL n){ //求b的逆元
LL x,y;
LL d = exgcd(b,n,x,y);
if(d==1) return (x%n + n)%n;
else return -1;
}
int main(){
LL T,n ,b;
cin >> T;
for(LL i = 1;i<= T; i++){
scanf("%lld%lld",&n,&b);
LL y = inv(b,MD);
printf("%lld\n",(n * y)%MD);
}
return 0;
}
2 当m为质数的时候直接使用费马小定理,m非质数使用欧拉函数。
利用欧拉定理b^phi(n)≡1(mod n)
b的逆元为b^-1 mod n=(b^(phi(n)-1)mod n+n)mod n
#include<iostream>
#include<cmath>
#include<cstdio>
using namespace std;
typedef long long LL;
const int MD = 9973;
int quickMod(int x,int y){//快速幂
int ans=1;
x %= MD;
while(y){
if(y&1) ans = (ans * x) % MD;
y = y/2;
x = (x * x)%MD;
}
return ans;
}
int eular(int x){ //求x的欧拉函数
int ans = x;
for(int i = 2,t = sqrt(x); i <= t; i++ ){
if(x%i == 0){
ans = ans / i * (i-1);
while(x%i==0){
x = x / i;
}
}
}
if(x > 1) //对分解最后的那个质数进行处理
ans = ans / x * (x-1);
return ans;
}
LL inv(LL b,LL n){//利用欧拉定理求逆元
LL x,y;
LL fin = eular(n);
return quickMod(b,fin-1);
}
int main(){
LL T,n ,b;
cin >> T;
for(LL i = 1;i<= T; i++) {
scanf("%lld%lld",&n,&b);
LL y = inv(b,MD);
printf("%lld\n",(n * y)%MD);
}
return 0;
}
3 当n为质数的时候,神奇的线性方法。
但是题目数据中不能保证n为质数,因此不能使用此神奇方法,此方法详细解说见https://blog.csdn.net/xuechen_gemgirl/article/details/80332859
inv[1]=1;
for(int i=2;i<=n;i++)
inv[i]=(p-p/i)*inv[p%i]%p;