乘法逆元
乘法逆元
定义:
对于整数
则称
定理 1 :
a 在模m 意义下有乘法逆元的充要条件是:\gcd(a, m) = 1 即
a 与m 互质。
::::info[证明]{open}
- 必要性:如果
\gcd(a,m) = d > 1 ,假设存在逆元x 使得a \times x \equiv 1 (\bmod m) ,则a \times x = k \times m + 1 ,由于d 整除a 和m ,所以d 整除1 矛盾。 - 充分性:如果
\gcd(a,m) =1 ,根据扩展欧几里得,那么存在x,y 使得a \times x + m \times y = 1 ,于是a\times x \equiv 1 (\bmod m) ,即x 就是a 的逆元。 ::::
求解逆元的方法
方法 1 : 扩展欧几里得求解逆元
原理:把求解逆元的同余方程转化为不定方程求解。
优点:可以又判断误解的步骤,并且非常实用。
时间复杂度:
方法 2 :费马小定理
我们需要明确前提:仅当模数为质数且
如果
转化一下可以发现:
所以
方法 3 : 线性递推求解逆元
如果我们需要求解
设
则有:
::::info[证明]{open}
令
在模
两边同乘
因此:
将
注意边界条件:
方法 4 : 阶乘逆元的线性求解
如果我们需要求解
令
::::info[证明]{open} 因为易得:
所以同时将两边取逆元,得:
得证。 ::::
例题
P1082
这是一道求逆元的模板题,发现数据范围非常大,且没有说一定互质,所以不能使用费马小定理,所以需要使用扩展欧几里得求解。
::::info[Code]
#include <bits/stdc++.h>
using namespace std;
#define int long long
int T, a, b, c, x, y;
int exgcd(int a, int b, int &x, int &y) {
if (b == 0) {
x = 1, y = 0;
return a;
}
int x1, y1;
int d = exgcd(b, a % b, x1, y1);
x = y1;
y = x1 - (a / b) * y1;
return d;
}
signed main() {
cin >> a >> b;
int d = exgcd(a, b, x, y);
cout << ((x % b + b) % b ? (x % b + b) % b : b) << '\n';
return 0;
}
::::
P3811
我们发现这道题的
::::info[Code]
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 3e6 + 5;
int n, p, inv[N];
void getinv() {
inv[1] = 1;
for (int i = 2; i < N; i++) inv[i] = (p - p / i) * inv[p % i] % p;
}
signed main() {
cin >> n >> p;
getinv();
for (int i = 1; i <= n; i++) cout << inv[i] << '\n';
return 0;
}
::::
P2265
题意非常简洁明了,直接说做法。
分析答案,因为只能向左或者向上,所以总共的步数为
那么组合数的求解是基本功了,直接使用逆元算就完了。
::::info[Code]
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e6 + 5, mod = 1e9 + 7;
int n, m, inv[N], infa[N];
int qpow(int x, int y) {
int ans = 1;
while (y) {
if (y & 1) ans = ans * x % mod;
x = x * x % mod;
y >>= 1;
}
return ans;
}
int fac(int x) {
int ans = 1;
for (int i = 1; i <= x; i++) ans = ans * i % mod;
return ans;
}
int C(int n, int m) {
if (n < 0 || m < 0 || n < m) return 0;
return fac(n) * qpow(fac(n - m), mod - 2) % mod * qpow(fac(m), mod - 2) % mod;
}
signed main() {
cin >> n >> m;
cout << C(n + m, n) << '\n';
return 0;
}
::::