AcWing 876. 快速幂求逆元
. 快速幂求逆元
一、题目描述
给定 组 ,其中 是质数,求 模 的乘法逆元,若逆元不存在则输出 impossible
。
注意:请返回在 之间的逆元。
乘法逆元的定义
输入格式
第一行包含整数 。
接下来 行,每行包含一个数组 ,数据保证 是质数。
输出格式
输出共 行,每组数据输出一个结果,每个结果占一行。
若 模 的乘法逆元存在,则输出一个整数,表示逆元,否则输出 impossible
。
数据范围
输入样例:
3
4 3
8 5
6 3
输出样例:
1
2
impossible
二、逆元概念
序号 | 取模概念下的加减乘除 | 正确性 |
---|---|---|
1 | 正确 | |
2 | 正确 | |
3 | 正确 | |
4 | 错误 |
:为什么除法错的?
证明是对的难,证伪的只要举一个反例:
对于一些题目,我们必须在中间过程中进行求余,否则数字太大,电脑存不下,那如果这个算式中出现除法,会损失精度,导致答案错误。
因为除法在取模运算时没有性质 ,这个是不成立的,没法计算了,这时数学家提出了个 逆元 的概念:
比如 ,因为在乘法模的世界里的逆元是,所以转化为 ,就可转化为乘法的性质了,就方便计算了,就是答案。
逆元可以代替除法,除以这个数就等于乘以这个数的逆元。
:怎么理解逆元的含义?
想像你在一个加法的世界里,以为世界的中心。有一天,你从世界的中心位置,进行了,突然,你想回到世界的中心,而你只能使用加法,所以你需要找一个数,让你加上这个数后,可以回到这个世界的中心,这个数就是你在加法世界里的逆元,也就是。
想像你在一个乘法取模的世界里,以为世界的中心,有一天,你从世界的中心位置,进行了的操作,突然,你想回到世界的中心,而你只能使用乘法取模的,所以你需要找到一个数,让你乘上它再模后,回到世界的中心,那么这个数就称为你在乘法模世界的逆元。
举个栗子, 那么和就在乘法世界里互为逆元,就像是加法世界里的和一样。
在这个乘法模的世界里,逆元不是唯一的,比如 而 。我们所说的求逆元一般是指逆元当中最小的那个。
三、费马小定理
内容:
当为质数时
证明:略
举个栗子:
今天是周一,再过 次方天,是周几呢?
解:因为一周天,其实是在求。
此时,是质数,可以用费马小定理计算同余结果:
计算与的关系,
所以 就是
今天是周一,再过四天就是周五了。
四、怎样求逆元?
-
当为质数时,可以用费马小定理+快速幂求逆元:
就是的逆元。
-
当不是质数时,可以用扩展欧几里得算法求逆元: (学习到这里时先不用理会这个内容)
有逆元的充要条件是与互质,所以
假设的逆元为,那么有
等价:
五、实现代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
// 快速幂 (a^k)%p
int qmi(int a, int k, int p) {
int res = 1;
while (k) {
if (k & 1) res = res * a % p;
a = a * a % p;
k >>= 1;
}
return res;
}
signed main() {
int n;
cin >> n;
while (n--) {
int a, p;
cin >> a >> p;
if (a % p == 0)
puts("impossible"); // 不互质
else
printf("%lld\n", qmi(a, p - 2, p));
}
}
六、费马小定理练习
: 求
这个幂指数很大,我们不会傻傻的真的去计算出,而是利用费马小定理对指数进行缩减:
因为是质数,所以使用费马小降幂,
在里面,把所有的倍数去掉,就是
也就是说,我们可以把原来的降为,最终的答案是一样的结果。
一个多位数有位,它左边三位为。当这个数最大时,它被除所得的余数是_______。
在小学奥数中,经常会用到费马小定理解决余数问题。
-
先举一个简单的例子:
比如计算 除以的余数是多少。
在这里,, 是素数,根据所以使用费马小定理,我们知道除以余,即:
所以答案就是:。
-
回到这个问题:
这个位数最大时即为后面是个。
我们设这个数是, 则。
这个问题就转变为除以的余数是多少?
我们用同余运算和费马小定理有:
这里因为去除,商是,余数是。商不会影响同余运算,舍去,保留余数
答案就是:
怎么样,超简单吧?那么,如果不是素数时,这种问题又该怎么处理呢?
这时候欧拉定理和欧拉函数就要大显身手啦。
:

解题思路
因为模数是,比较小,而幂是,很大!所以使用费马小降幂,这里就是;
int n = 1, ans = 0;
for (int i = 1; i <= 2019; i++) n = n * 2019 % 100;
也就是说,在不断的循环计算的过程中,我们利用费马小定理,找到了一个,使得与原数对于结果的贡献是一样的,但明显小于,方便计算出来结果。
实现代码
#include <bits/stdc++.h>
using namespace std;
int main() {
int n = 1, ans = 0;
for (int i = 1; i <= 2019; i++) n = n * 2019 % 100; //计算出n'
// 1~11的n次方
for (int i = 1; i <= 11; i++) {
int x = 1;
for (int j = 1; j <= n; j++) x = x * i % 101; //降幂后可以正常按要求计算
//收集答案
ans += x;
}
//最终也要模一下101
printf("%d\n", ans % 101);
return 0;
}
:

解题思路
项数比大很多,比小,所以不用费马小定理,用循环周期+快速幂做

实现代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL mod = 10086;
LL pow_mod(LL x, LL p) {
LL res = 1;
while (p) {
if (p & 1) res = res * x % mod;
p >>= 1;
x = x * x % mod;
}
return res;
}
LL ans;
LL n = 1e12;
int main() {
for (int i = 1; i <= mod; i++) ans = (ans + pow_mod(i, 2019)) % mod; //求到10086 一个循环周期的长度
ans = ans * (n / mod) % mod; //乘上倍数
n %= mod; //再加上余数
for (int i = 1; i <= n; i++) ans = (ans + pow_mod(i, 2019)) % mod;
printf("%lld\n", ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!