乘法逆元 [数学]
定义
逆元素是指一个可以取消另一给定元素运算的元素
—百度百科
简单说 就是 a∗a−1=1 一样 在模 意义下有更多性质
常见求逆元的方法
1.拓展欧几里得求逆元
ps必须 a,p互质
复杂度 logp
#include <bits/stdc++.h>
using namespace std;
const int maxn = 3e6 + 10;
long long n, m;
long long exgcd(long long a, long long b, long long &x, long long &y) {
if (b == 0) {
x = 1, y = 0;
return a;
}
int d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
signed main() {
ios::sync_with_stdio(0);
long long a, b;
cin >> a >> b;
long long x = 0, y = 0;
exgcd(a, b, x, y);
cout << (x % b + b) % b << endl;
return 0;
}
2.欧拉定理求逆元
也是 a p 必须互质
欧拉值 求法只要按照 公式直接出就好
复杂度 log(phi(p))+sqrt(n)
#include <bits/stdc++.h>
using namespace std;
const int maxn = 3e6 + 10;
long long n, m;
long long pow_mod(long long a, long long b, long long p) {
long long res = 1;
for(; b; b >>= 1) {
if(b & 1) res = res * a % p;
a = a * a % p;
}
return res;
}
long long phi(long long x, long long p) {
long long res = x, tmp = x;
for(int i = 2; i * i <= tmp; i ++) {
if(x % i == 0) {
res = (res / i) % p * ((i - 1 % p)) % p;
while(x % i == 0) x /= i;
}
}
if(x > 1) res = (res / x) % p * ((x - 1) % p) % p;
return res;
} // 一般欧拉值 也不太可能比p大 板子就写下吧 一般也不需要
long long phi(long long x) {
long long res = x, tmp = x;
for(int i = 2; i * i <= tmp; i ++) {
if(x % i == 0) {
res = res / i * (i - 1);
while(x % i == 0) x /= i;
}
}
if(x > 1) res = res / x * (x - 1);
return res;
}
signed main(){
cin >> a >> b;
long long tmp = phi(b) - 1;
cout << pow_mod(a, tmp, b) << endl;
return 0;
}
3.费马小定理求逆元
只要p 是质数 就行 不要求互质
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 10;
long long n, m;
long long pow_mod(long long a, long long b, long long p) {
long long res = 1;
for(; b; b >>= 1) {
if(b & 1) res = res * a % p;
a = a * a % p;
}
return res;
}
const int p = 19260817;
template<typename T>
inline void read(T &x) {
x = 0;
char ch = getchar();
bool f = false;
while(!isdigit(ch)) {
f |= (ch == '-');
ch = getchar();
}
while(isdigit(ch)) {
x = (x << 1) + (x << 3) + (ch ^ 48);
x %= p;
ch = getchar();
}
x = f ? -x : x;
}
signed main() {
long long a, b;
read(a), read(b);
if(!b) puts("Angry!");
else cout << a * pow_mod(b, p - 2, p) % p << endl;
return 0;
}
线性求逆元
也算是 打表 不过之后我们通过类似 阶乘逆元的方式 可以 看出 前缀积的逆元就是逆元的前缀积(之后细讲)
线性求逆元模板
#include <bits/stdc++.h>
using namespace std;
const int maxn = 3e6 + 10;
int n, m;
long long pow_mod(long long a, long long b, long long p) {
long long res = 1;
for(; b; b >>= 1) {
if(b & 1) res = res * a % p;
a = a * a % p;
}
return res;
}
int inv[maxn], p;
signed main() {
scanf("%d %d", &n, &p);
inv[1] = 1;
cout << inv[1] << endl;
for(int i = 2; i <= n; i ++) {
inv[i] = 1ll * (p - p / i) * inv[p % i] % p;
printf("%d\n", inv[i]);
}
return 0;
}
阶乘 O(n) 逆元
这里 证明 一并放到 乘法逆元2 中
#include <bits/stdc++.h>
using namespace std;
const int maxn = 3e6 + 10;
long long n, m;
long long pow_mod(long long a, long long b, long long p) {
long long res = 1;
for(; b; b >>= 1) {
if(b & 1) res = res * a % p;
a = a * a % p;
}
return res;
}
long long inv[maxn], p;
long long f[maxn], c[maxn];
signed main() {
scanf("%lld %lld", &n, &p);
c[1] = c[0] = 1;
for(int i = 2; i <= n; i ++)
c[i] = c[i - 1] * i % p;
f[n] = pow_mod(c[n], p - 2, p);
for(int i = n - 1; i >= 1; i --) {
f[i] = f[i + 1] * (i + 1) % p;
}
for(int i = 1; i <= n; i ++)
printf("%lld\n", f[i] * c[i - 1] % p);
return 0;
}
乘法逆元2
注释部分T了 2333
#include <bits/stdc++.h>
using namespace std;
int p, n, k;
const int maxn = 5e6 + 10;
template<typename T>
inline void read(T &x) {
x = 0;
char ch = getchar();
bool f = false;
while(!isdigit(ch)) {
f |= (ch == '-');
ch = getchar();
}
while(isdigit(ch)) {
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
x = f ? -x : x;
}
long long pow_mod(long long a, long long b, long long p) {
long long res = 1;
for(; b; b >>= 1) {
if(b & 1) res = res * a % p;
a = a * a % p;
}
return res;
}
int b[maxn], s[maxn], a[maxn];
int main() {
read(n), read(p), read(k);
int ans = 0;
// for(int i = 1, a; i <= n; i ++) {
// read(a);
// ans = (1ll * ans + pow_mod(k, i, p) * pow_mod(a, p - 2, p)) % p ;
// }
// printf("%d\n", ans);
s[0] = 1;
b[n + 1] = 1;
for(int i = 1; i <= n; i ++) {
read(a[i]);
s[i] = 1ll * s[i - 1] * a[i] % p;
}
b[n + 1] = pow_mod(s[n], p - 2, p);
for(int i = n; i >= 1; i --) {
b[i] = 1ll * b[i + 1] * a[i] % p;
}
int tmpk = k;
for(int i = 1; i <= n; i ++) {
ans = (ans + (1ll * b[i + 1] * s[i - 1]) % p * tmpk) % p;
tmpk = 1ll * tmpk * k % p;
}
printf("%d\n", ans);
return 0;
}