数据结构与算法学习笔记----快速幂

数据结构与算法学习笔记----快速幂

@@ author: 明月清了个风

@@ first publish time: 2025.1.2

ps⭐️快速幂的两道模版题,快速幂,乘法逆元,费马小定理

Acwing 875. 快速幂

原题链接\]([875. 快速幂 - AcWing题库](https://www.acwing.com/problem/content/877/)) 给定 n n n组 a i , b i , p i a_i,b_i,p_i ai,bi,pi,对于每组数据,求出 a i b i    m o d    p i a_i\^{b_i} \\; mod \\; p_i aibimodpi的值 #### 输入格式 第一行包含整数 n n n, 接下来 n n n行,每行包含三个整数 a i , b i , p i a_i,b_i,p_i ai,bi,pi。 #### 输出格式 对于每组数据,输出一个结果,表示 a i b i    m o d    p i a_i\^{b_i} \\; mod \\; p_i aibimodpi的值。 每个结果占一行。 #### 数据范围 1 ≤ n ≤ 100000 1 \\le n \\le 100000 1≤n≤100000, 1 ≤ a i , b i , p i ≤ 2 × 1 0 9 1 \\le a_i,b_i,p_i \\le 2\\times 10\^9 1≤ai,bi,pi≤2×109、 #### 思路 快速幂是一种高效的计算整数幂的算法,适用于大数的幂运算,比如这题的数据范围, a i , b i , p i a_i,b_i,p_i ai,bi,pi都可以到亿的级别,通过快速幂可以将暴力运算的 O ( n ) O(n) O(n)时间复杂度优化到 O ( log ⁡ n ) O(\\log n) O(logn)级别, n n n是幂的范围。 **核心思想** :将指数 b b b通过二进制分解,从而达到 log ⁡ b \\log b logb的运算量,也就是 a b    m o d    p = a ( 2 x 1 ) + 2 x 2 + ⋯ + 2 x k    m o d    p = a 2 x 1 a 2 x 2 ⋯ a 2 x k    m o d    p = ( a 2 x 1 m o d    p ) ⋅ ( a 2 x 2 m o d    p ) ⋯ ( a 2 x k m o d    p ) \\begin{align\*} a\^b \\; mod \\; p \& = a\^{(2\^{x_1}) + 2\^{x_2} + \\cdots + 2\^{x_k}} \\; mod \\; p \\\\ \& = a\^{2 \^ {x_1}} a\^{2 \^ {x_2}} \\cdots a\^{2\^{x_k}} \\; mod \\; p \\\\ \& = (a\^{2\^{x_1}}mod \\;p)\\cdot(a\^{2\^{x_2}}mod \\;p) \\cdots(a\^{2\^{x_k}}mod \\;p) \\\\ \\end{align\*} abmodp=a(2x1)+2x2+⋯+2xkmodp=a2x1a2x2⋯a2xkmodp=(a2x1modp)⋅(a2x2modp)⋯(a2xkmodp) 在代码中,我们也无需对 k k k的二进制分解及 a x a\^x ax进行预处理,只需要边运算边处理就行,具体看下面代码吧,代码还是很清晰的。 #### 代码 ```cpp #include #include using namespace std; typedef long long LL; int qmi(int a, int k, int p) { int res = 1; while(k) { if(k & 1) res = (LL) res * a % p; // 从k的二进制表示最低位开始,也就是2的0次方,此时a的2的0次方就是a k >>= 1; // 每次右移一位 a = (LL) a * a % p; // 每次将a平方,因为a上面是2的次方,每次提高一位相当于乘一个a的2的0次方,就是a } return res; } int main() { int n; cin >> n; while(n --) { int a, b, p; cin >> a >> b >> p; cout << qmi(a, b, p) << endl; } return 0; } ``` ### Acwing 876. 快速幂求逆元 \[原题链接\]([876. 快速幂求逆元 - AcWing题库](https://www.acwing.com/problem/content/878/)) 给定 n n n组 a i , p i a_i,p_i ai,pi,其中 p i p_i pi是质数,求 a i a_i ai模 p i p_i pi的乘法逆元,若逆元不存在则输出`impossible`。 注意:请返回 0 ∼ p − 1 0 \\sim p - 1 0∼p−1之间的逆元。 **乘法逆元的定义**: > 若整数 b , m b,m b,m互质,并且对于任意的整数 a a a,如果满足 b ∣ a b\|a b∣a,则存在一个整数 x x x,使得 a b ≡ a × x ( m o d    m ) \\frac{a}{b} \\equiv a \\times x (mod \\; m) ba≡a×x(modm),则称 x x x为 b b b模 m m m的乘法逆元,记为 b − 1 ( m o d    m ) b\^{-1}(mod \\; m) b−1(modm)。 > > b b b存在乘法逆元的充要条件是 b b b与模数 m m m互质,当模数 m m m为质数时, b m − 2 b\^{m - 2} bm−2即为 b b b的乘法逆元。 #### 输入格式 第一行包含整数 n n n, 接下来 n n n行,每行包含一个数组 a i , p i a_i,p_i ai,pi,数据保证 p i p_i pi是质数。 #### 输出格式 输出共 n n n行,每组数据输出一个结果,每个结果占一行。 若 a i a_i ai模 p i p_i pi的乘法逆元存在,则输出一个整数表示逆元,否则输出`impossbile`。 #### 数据范围 1 ≤ n ≤ 1 0 5 1 \\le n \\le 10\^5 1≤n≤105, 1 ≤ a i , p i ≤ 2 ∗ 1 0 9 1 \\le a_i, p_i \\le 2 \* 10\^9 1≤ai,pi≤2∗109 #### 思路 主要的难点是定义比较绕,我们可以对定义的式子进行一些变形 a b ≡ a × x ( m o d    m ) (1) \\frac{a}{b} \\equiv a \\times x (mod \\; m) \\tag{1} ba≡a×x(modm)(1) 在式(1)的两边同乘 b b b,我们可以得到式(2) a ≡ b × a × x ( m o d    m ) (2) a \\equiv b \\times a \\times x (mod \\; m) \\tag{2} a≡b×a×x(modm)(2) 两边再同时除 a a a可以得到 1 ≡ b × x ( m o d    m ) (3) 1 \\equiv b \\times x (mod \\; m) \\tag{3} 1≡b×x(modm)(3) 因此我们要求的就是一个 x x x,可以使 b × x ≡ 1 ( m o d    m ) b \\times x \\equiv 1 (mod \\; m) b×x≡1(modm) 还有一个注意点是 b b b存在乘法逆元的充要条件是模数 m m m与 b b b互质,题目中给出了条件模数 p i p_i pi保证了是质数,也就是保证了这个条件。 这里需要补充一个额外的定理:**费马小定理** > 当 m m m是一个质数,对于任意整数 a a a( a a a不被 m m m整除),有 a m − 1 ≡ 1 ( m o d    m ) a\^{m- 1} \\equiv 1 (mod \\; m) am−1≡1(modm) 那么对费马小定理的这个公式进行变形,拆分次方可得 a ⋅ a m − 2 ≡ 1    ( m o d    m ) a \\cdot a\^{m - 2} \\equiv 1 \\; (mod \\; m) a⋅am−2≡1(modm),我们就能直接得到 a a a的乘法逆元是 a m − 2 a\^{m - 2} am−2。 因此问题转换为求 a m − 2 a\^{m - 2} am−2,也就是应用上面的快速幂。 需要注意的是题目虽然保证了 p p p是一个质数,但是却被没有保证我们的充要条件,也就是 a i a_i ai与 p i p_i pi互质,因此需要判断 a i a_i ai是否是 p i p_i pi的倍数,只有这样的情况他们才不互质。 #### 代码 ```cpp #include #include #include #include using namespace std; typedef long long LL; int qmi(int a, int k, int p) { int res = 1; while(k) { if(k & 1) res = (LL) res * a % p; k >>= 1; a = (LL) a * a % p; } return res; } int main() { int n; cin >> n; while(n --) { int a, p; cin >> a >> p; if(a % p == 0) puts("impossible"); else cout << qmi(a, p - 2, p) << endl; } return 0; } ```

相关推荐
云上艺旅12 小时前
K8S学习之基础七十四:部署在线书店bookinfo
学习·云原生·容器·kubernetes
你觉得20512 小时前
哈尔滨工业大学DeepSeek公开课:探索大模型原理、技术与应用从GPT到DeepSeek|附视频与讲义下载方法
大数据·人工智能·python·gpt·学习·机器学习·aigc
A旧城以西13 小时前
数据结构(JAVA)单向,双向链表
java·开发语言·数据结构·学习·链表·intellij-idea·idea
无所谓จุ๊บ13 小时前
VTK知识学习(50)- 交互与Widget(一)
学习·vtk
FAREWELL0007513 小时前
C#核心学习(七)面向对象--封装(6)C#中的拓展方法与运算符重载: 让代码更“聪明”的魔法
学习·c#·面向对象·运算符重载·oop·拓展方法
吴梓穆14 小时前
UE5学习笔记 FPS游戏制作38 继承标准UI
笔记·学习·ue5
Three~stone14 小时前
MySQL学习集--DDL
数据库·sql·学习
齐尹秦14 小时前
HTML 音频(Audio)学习笔记
学习
V---scwantop---信15 小时前
英文字体:大胆都市街头Y2Y涂鸦风格品牌海报专辑封面服装字体 Chrome TM – Graffiti Font
笔记·字体
瞌睡不来15 小时前
(学习总结32)Linux 基础 IO
linux·学习·io