HDU 1576 -- A/B (总结乘法逆元的几种求法)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1576

A/B

Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 7264    Accepted Submission(s): 5774

Problem Description

要求(A/B)%9973,但由于A很大,我们只给出n(n=A%9973)(我们给定的A必能被B整除,且gcd(B,9973) = 1)。

Input

数据的第一行是一个T,表示有T组数据。
每组数据有两个数n(0 <= n < 9973)和B(1 <= B <= 10^9)。

Output

对应每组数据输出(A/B)%9973。

Sample Input

2

1000 53

87 123456789

Sample Output

7922

6060

题意:给出A%9973和B的值,求(A/B)%9973。

解析:(A  /  B) % p = (A * inv(B) ) % p = (A % p * inv(B) % p) % p,其中p为模数9973, inv(B)为B关于模p的逆元。

同余式:

如果两个正整数a和b之差能被n整除,那么我们就说a和b对模n同余,记作:a≡b (mod n)。 
a≡b(mod n)等价于a与b分别用n去除,余数相同。

乘法逆元:

如果ax≡1 (mod p),且gcd(a,p)=1(a与p互质,此为一个数有逆元的充要条件,此时逆元唯一存在),则称a关于模p的乘法逆元为x。

逆元的含义:模n意义下,1个数a如果有逆元x,那么除以a相当于乘以x。

求解逆元的方法:

1. 扩展欧几里得算法:

已知整数a、b,扩展欧几里得算法可以在求得a、b的最大公约数的同时,能找到整数x、y(其中一个很可能是负数),使它们满足贝祖等式ax + by = gcd(a, b)。

当a关于模b的逆元存在,有gcd(a, b) == 1,即扩展欧几里得算法可求得x, y满足ax + by = 1, 两边同时余b,

ax % b + by % b = 1 % b

ax % b = 1 % b

ax ≡ 1(mod b)

所以x是a的模b乘法逆元,同理y是b的模a乘法逆元

算法时间复杂度:O(logn)

模板代码:

 1 int ex_gcd(int a, int b, int &x, int &y) {  // 函数返回gcd(a, b)
 2     if (b == 0) {
 3         x = 1, y = 0;
 4         return a;
 5     }
 6     int r = ex_gcd(b, a % b, y, x);
 7     y -= (a / b) * x;
 8     return r;
 9 }
10
11 int main() {
12     int a, b, x, y;
13     cin >> a >> b;  // 求a关于模b的逆元
14     cout << (ex_gcd(a, b, x, y) == 1 ? (x % b + b) % b : -1) << endl;  // -1表示逆元不存在
15
16     return 0;
17 }

2. 费马小定理:

内容:假如a是一个整数,p是一个质数,那么a- a是p的倍数,可以表示为

ap ≡ a (mod p)

如果a不是p的倍数(即gcd(a, p) == 1),这个定理也可以写成

ap-1 ≡ 1 (mod p)

变形得a * ap-2 ≡ 1 (mod p),所以a关于模p的逆元x = ap-2 (mod p),用快速幂模可快速求之。

算法时间复杂度:O(logn)

模板代码:

 1 LL pow_mod(LL a, LL b, LL p) {    //a的b次方取模p
 2     LL ret = 1;
 3     while (b) {
 4         if(b & 1) ret = (ret * a) % p;
 5         a = (a * a) % p;
 6         b >>= 1;
 7     }
 8     return ret;
 9 }
10 LL Fermat(LL a, LL p) {   //费马小定理求a关于b的逆元
11         return pow_mod(a, p-2, p);
12 }

3. 欧拉定理:

欧拉函数:对正整数n,欧拉函数是小于n的正整数中与n互质的数的数目(φ(n) = n(1 – 1/p1)(1 – 1/p2)…(1 – 1/pm), pn为n的所有质因数,  φ(1) = 1)。

欧拉定理:若gcd(a, p) = 1,则a^φ(p) ≡ 1 (mod p)。

即 a*a^(φ(p)?1) ≡ 1(mod p),那么a关于模p的逆元x = a^(φ(p)?1) (mod p)

(当p为素数的时候φ(p)=p-1,则φ(p)-1=p-2可以看出欧拉定理是费马小定理的推广)

费马小定理要求模数p为素数,欧拉定理则没有此要求,但是似乎还有个问题?如何判断a是否有逆元呢?

再求一次gcd判断是否互质吗?这还不如直接用扩展欧几里得算法呢。

可以由逆元性质直接检验是否为逆元,看求出的值x与a相乘模p是否为1即可。

算法时间复杂度:O(logn)

模板代码:

 1 #include <iostream>
 2 using namespace std;
 3 typedef long long LL;
 4 LL euler(LL n) {  // 欧拉函数
 5     LL res = n, i;
 6     for (i = 2; i * i <= n; i++) {
 7         if (n % i == 0) {
 8             res = res / i * (i - 1);
 9             while (n % i == 0) n /= i;
10         }
11     }
12     if (n != 1) res = res / n * (n - 1);
13     return res;
14 }
15 LL pow_mod(LL a, LL b, LL p) {  // 快速幂模
16     LL ret = 1;
17     while (b) {
18         if(b & 1) ret = (ret * a) % p;
19         a = (a * a) % p;
20         b >>= 1;
21     }
22     return ret;
23 }
24
25 int main() {
26     LL a, p, inv;
27     cin >> a >> p;
28     inv = pow_mod(a, euler(p) - 1, p); // inv为a关于模p的逆元
29     if (a * inv % p == 1) cout << inv << endl;  // 由逆元性质检验逆元是否存在
30     else cout << -1 << endl;  // 不存在输出-1
31
32     return 0;
33 }

4. O(n)求1~n逆元表

在模质数p下,求1~n逆元(n < p)

inv(a) = (p - p / a) * inv(p % a) % p

证明:

设x = p % a,y = p / a
于是有 x + y * a = p
(x + y * a) % p = 0
移项得 x % p = (-y) * a % p
x * inv(a) % p = (-y) % p
inv(a) = (p - y) * inv(x) % p
于是 inv(a) = (p - p / a) * inv(p % a) % p

模板代码:

 1 #include<cstdio>
 2 const int N = 200000 + 5;
 3 const int MOD = (int)1e9 + 7;
 4 int inv[N];
 5 int init(){
 6     inv[1] = 1;
 7     for(int i = 2; i < N; i ++){
 8         inv[i] = (MOD - MOD / i) * 1ll * inv[MOD % i] % MOD;
 9     }
10 }
11 int main(){
12     init();
13 }

总结:

1. 逆元求解一般利用扩欧。

2. 当模数p为质数的时候直接使用费马小定理。

3. p非质数使用欧拉函数(一般不用)。

HDU 1576 -- A/B   AC代码:

 1 #include <iostream>
 2 #define MOD 9973
 3 using namespace std;
 4 typedef long long LL;
 5 LL qmod(LL a, LL b) {   // 快速幂模
 6     LL res = 1;
 7     while (b) {
 8         if (b & 1) res = res * a % MOD;
 9         a = a * a % MOD;
10         b >>= 1;
11     }
12     return res;
13 }
14
15 int main() {
16     int t; cin >> t;
17     while (t--) {
18         int n, b; cin >> n >> b;
19         cout << 1ll * n * qmod(1ll * b, MOD - 2) % MOD << endl;  // 费马小定理求b的逆元
20     }
21     return 0;
22 }

进阶题:HDU 5976 -- Detachment (贪心+逆元表+前缀和、积):

 1 #include <stdio.h>
 2 const int maxn = 1e5 + 10;
 3 const int MOD = 1e9 + 7;
 4 typedef long long LL;
 5 LL mul[maxn], sum[maxn], inv[maxn];
 6 void init() {
 7     mul[1] = 1;
 8     sum[1] = 0;
 9     inv[1] = 1;
10     for (int i = 2; i < maxn; i++) {
11         sum[i] = sum[i-1] + i;
12         mul[i] = (i * mul[i-1]) % MOD;
13         inv[i] = (MOD - MOD / i) * 1ll * inv[MOD % i] % MOD;
14     }
15 }
16
17 int main() {
18     int t, x;
19     init();
20     scanf("%d", &t);
21     while (t--) {
22         scanf("%d", &x);
23         if (x == 1) { puts("1"); continue; }
24         int l = 2, r = maxn, mid, idx;
25         while (l <= r) {
26             mid = (l + r) / 2;
27             if (sum[mid] <= x) idx = mid, l = mid + 1;
28             else r = mid - 1;
29         }
30         int rest = x - sum[idx];
31         LL ans = 0;
32         if (rest == idx) ans = (mul[idx] * inv[2] % MOD * (idx + 2)) % MOD;
33         else ans = mul[idx+1] * inv[idx+1-rest] % MOD;
34         printf("%I64d\n", ans);
35     }
36     return 0;
37 }

原文地址:https://www.cnblogs.com/kangkang-/p/8437080.html

时间: 2024-10-13 19:50:10

HDU 1576 -- A/B (总结乘法逆元的几种求法)的相关文章

求乘法逆元的几种方法

(数学渣,下面的文字可能有误,欢迎指教)乘法逆元的定义貌似是基于群给出的,比较简单地理解,可以说是倒数的概念的推广.记a的关于模p的逆元为a^-1,则a^-1满足aa^-1≡ 1(mod p) 加减乘与模运算的顺序交换不会影响结果,但是除法不行.有的题目要求结果mod一个大质数,如果原本的结果中有除法,比如除以a,那就可以乘以a的逆元替代. 在mod p的运算中,a存在乘法逆元当且仅当a与p互质.一般题目给的是一个大质数,所以只要a不是p的倍数,就以求乘法逆元. 目前了解到的求法有三种:1.扩展

hdu - 3049 - Data Processing(乘法逆元)

题意:N(N<=40000)个数n1, n2, ..., nN (ni<=N),求(2 ^ n1 + 2 ^ n2 + ... + 2 ^nN) / N % 1000003. 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3049 -->>RJ白书上说"由于'乘法逆'太重要了--",上一年南京区赛同学也碰到了求逆元--现在,学习了.. 什么是乘法逆?ab % m = 1 (这里的 a, b 分别都是模 m 的同余等

乘法逆元...Orz

最近打的几场比赛,都出现了有关逆元的题目,今天就整理了一下... 求乘法逆元的几种方法:http://www.cnblogs.com/james47/p/3871782.html 博文转载链接:http://blog.csdn.net/acdreamers/article/details/8220787 今天我们来探讨逆元在ACM-ICPC竞赛中的应用,逆元是一个很重要的概念,必须学会使用它. 对于正整数和,如果有,那么把这个同余方程中的最小正整数解叫做模的逆元. 逆元一般用扩展欧几里得算法来求

求逆元的四种算法(拓欧费马小线性推欧拉)

求逆元的四种算法 拓展欧几里得算法求逆元 上一篇博客中已经讲过拓展欧几里得算法,并且讲解了求逆元的原理.这里只列出代码 在要求逆元的数与p互质时使用 代码 //扩展欧几里得定理 int ex_gcd(int a,int b,int& x,int& y) { if(b==0) { x=1; y=0; return a; } int ans = ex_gcd(b,a%b,x,y); int tmp = x; x = y; y = tmp-a/b*y; return ans; } int cal

HDU 1576 (乘法逆元)

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1576 题目大意:求(A/B)mod 9973.但是给出的A是mod形式n,n=A%9973. 解题思路: 两种思路,一种从乘法逆元角度,另一种从扩展GCD推公式角度. ①乘法逆元: 先来看下逆元和乘法逆元的关系,对于A*X=B,有X=A-1*B,A-1就是普通的逆元了,在这里就是倒数. 如果A*X=B mod n,变成同余式了,那么A-1依然是存在的,只不过不是倒数了,一般把同余之后的逆元称为乘法

HDU 1452 (约数和+乘法逆元)

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1452 题目大意:求2004^X所有约数和,结果mod 29. 解题思路: ①整数唯一分解定理: 一个整数A一定能被分成:A=(P1^K1)*(P2^K2)*(P3^K3).....*(Pn^Kn)的形式.其中Pn为素数. 如2004=(22)*3*167. 那么2004x=(22x)*(3x)*(167x). ②约数和公式 对于一个已经被分解的整数A=(P1^K1)*(P2^K2)*(P3^K3)

hdu 1452 Happy 2004 (快速幂+取模乘法逆元)

Problem Description Consider a positive integer X,and let S be the sum of all positive integer divisors of 2004^X. Your job is to determine S modulo 29 (the rest of the division of S by 29).Take X = 1 for an example. The positive integer divisors of

HDU 4828 Grids(卡特兰数+乘法逆元)

首先我按着我的理解说一下它为什么是卡特兰数,首先卡特兰数有一个很典型的应用就是求1~N个自然数出栈情况的种类数.而这里正好就对应了这种情况.我们要满足题目中给的条件,数字应该是从小到大放置的,1肯定在左上角,所以1入栈,这时候我们放2,如果我们把2放在了1的下面就代表了1出栈,把2放在上面就代表了2也进栈(可以看一下hint中第二组样例提示),以此类推,这样去放数,正好就对应了上面一行入栈,下面一行出栈的情况,一共n行,对应上限为n的卡特兰数. 需要注意的地方就是在使用卡特兰数递推式的时候,除法

HDU 5895 Mathematician QSC(矩阵乘法+循环节降幂+除法取模小技巧+快速幂)

传送门:HDU 5895 Mathematician QSC 这是一篇很好的题解,我想讲的他基本都讲了http://blog.csdn.net/queuelovestack/article/details/52577212 [分析]一开始想简单了,对于a^x mod p这种形式的直接用欧拉定理的数论定理降幂了 结果可想而知,肯定错,因为题目并没有保证gcd(x,s+1)=1,而欧拉定理的数论定理是明确规定的 所以得另谋出路 那么网上提供了一种指数循环节降幂的方法 具体证明可以自行从网上找一找 有