逆元

为什么会有逆元这个概念。

因为 有时我们需要计算  (A/B) % M  的值, 如果B过大,或者A过大,可能会爆精度,然后我们就想到能不(A%M / B%M)

发现这种方法不对,那我们可以将除法换成乘法(A * B^{-1})% M 这样就能转化成 (A % M *B^{-1} % M) % M 

那我们如何来求B^{-1} 呢?

B^{-1} 在就是B 的倒数,但是我们求 B^{-1}虽然不完全是倒数,B^{-1}是逆元 (倒数的扩展)

B的倒数就B^{-1} 是 B 对 M 取模 等于 1 。

然后我们推一下 (a/b)%m

设c是b的逆元,则有b*c≡1(mod m);

则(a/b)%m = (a/b)*1%m = (a/b)*(b*c%m)%m= a*c(mod m);

即a/b的模等于a*b的逆元的模;

求逆元

求一个数的逆元(欧几里得,a 和 m 互质)

求多个数的逆元(线性筛选)

求一个数的逆元也可以用费马小定理(时间线性且 m为质数)

逆元求法1

通过扩展欧几里得 来求

限制 a  与 m  互质  

时间复杂度  O(logn)。

原理: 求  a 关于 m 的逆元

a * x = 1( mod m)

可以化简为 

a * x = m * y + 1

a * x - m * y = 1

是不是一个二元方程求解 ? 直接上扩展欧几里得求解

ll ex_gcd(ll a, ll b, ll &x, ll &y){
	if(!b){
		x = 1;
		y = 0;
		return a;
	}
	ll d = ex_gcd(b, a%b, x, y);
	ll t = x;
	x = y;
	y = t - (a/b) * y;
	return d;
} 
ll inv(ll a, ll m){
	ll x, y;
	ll d = ex_gcd(a, m, x, y);
	x = (x % m + m) % m; 
	return x;
}

逆元求法2

费马小定理

有数据范围限制 因为用到快速幂

限制 1 <= x <= 1e9  而且 m 为质数

a^{m-2}   就是 a 的逆元

时间复杂度 O(logn)

ll quick_pow(ll a, ll b){
	if(b < 0) return 0;
	ll ret = 1;
	 a %= m;
	 while(b){
	 	if( b & 1) ret = (ret * a) % m;
	 	b >>= 1;
	 	a = (a * a) % m;
	 }
	 return ret;
}
ll inv(ll a){
	return quick_pow(a, m - 2);
}

逆元求法 3

逆元线性筛选

限制 : 求1,2,...,N关于 M 的逆元(M为质数)

时间复杂度 O (N)

const int mod = 1000000009;
const int maxn = 10005;
int inv[maxn];
inv[1] = 1;
for(int i = 2; i < 10000; i++)
    inv[i] = inv[mod % i] * (mod - mod / i) % mod;

逆元求法4

求阶乘的逆元

inv[maxn]=mod_pow(fac[maxn],mod-2);    // fac[] 是阶乘数组
for(ll i=maxn-1;i>=0;i--)
    inv[i]=(inv[i+1]*(i+1))%mod;
  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值