洛谷 P3811 【模板】乘法逆元(欧拉定理&&线性求逆元)

题目传送门

逆元定义

逆元和我们平时所说的倒数是有一定的区别的,我们平时所说的倒数是指:a*(1/a) = 1,那么逆元和倒数之间的区别就是:假设x是a的逆元,那么 a * x = 1(mod p),也就是只多了一个取余的操作,这个取余的操作,就会保证a的逆元不一定只是a的倒数。那么我们的逆元有什么作用呢?

并且取余还不满足下面式子:( a/b )%p = (a%p  /  b%p)  %  p ,那么我们如果遇到b过大必须在中间过程进行取余的操作,那么我们会发现在乘法中满足:(a*b) % p = (a%p  *  b%p) %p,那么我们只要将上面式子转换为下面乘法的式子就可以了

我们用inv(b)来表示b的逆元,那么他一定满足:b*inv(b) = 1(mod p)    ==>  b = 1/inv(b) ,那么我们代入上面的除法的式子:(a/b)%p =     (a * inv(b)) %p = (a%p  *  inv(b)%p) % p

这样我们就可以根据逆元来将除法取余的式子转换为乘法取余的式子
原文:https://blog.csdn.net/li1615882553/article/details/80001473

一:欧拉定理求逆元

 1 #include<iostream>
 2 #include<cstdio>
 3 
 4 using namespace std;
 5 
 6 long long m,k,n,sum,s;
 7 
 8 inline long long phi(long long x) {
 9     long long res = x,a = x;
10     for(int i = 2;i * i <= a; i++)
11         if(a % i == 0) {
12             res = res / i * (i - 1);
13             while(a % i == 0)
14                 a = a / i;
15         }
16     if(a > 1)
17         res = res / a * (a - 1);
18     return res;
19 }
20 
21 inline void _out(long long pp,long long v) {
22     sum = 1;
23     while(pp > 0) {
24         if(pp % 2 != 0)
25             sum = (sum * v) % m;
26         pp = pp / 2;
27         v = (v * v) % m;
28     }
29     printf("%d
",sum);
30 }
31 
32 int main() {
33     scanf("%d%d",&n,&m);
34     k = phi(m);
35     s = k - 1;
36     for(int i = 1;i <= n; i++)
37         _out(s,i);
38     return 0; 
39 }
欧拉定理

但因为欧拉定理求逆元时间复杂度为O(nlongn),所以本题会被卡两个点。

二:线性求逆元

 1 #include<iostream>
 2 #include<cstdio>
 3 
 4 using namespace std;
 5 
 6 int n,inv[3000001];
 7 long long p;
 8 
 9 int main() {
10     inv[1] = 1;
11     scanf("%d%lld",&n,&p);
12     for(int i = 2;i <= n; i++) 
13         inv[i] = (p - p / i) * inv[p % i] % p;
14     for(int i = 1;i <= n; i++)
15         printf("%d
",inv[i]);
16     return 0;
17 }
线性

因为是线性,O(n)足够优秀,所以轻松过掉本题

原文地址:https://www.cnblogs.com/lipeiyi520/p/11267186.html