快速幂
1. 快速幂原理
快速幂
- 快速幂需要解决的问题是:对于给定正整数a、b、p( 1 ≤ a 、 b 、 p ≤ 1 0 9 1 \le a、b、p \le 10^9 1≤a、b、p≤109,p是任意正整数,不一定是质数),让我们求解 a b m o d p a^b \ mod \ p ab mod p的值。
- 核心思想是将b看成一个二进制的数。具体求解分为两步:
- (1)首先我们可以预处理出如下值:
a 2 0 m o d p a 2 1 m o d p . . . a 2 ⌊ l o g ( k ) ⌋ m o d p a^{2^0} \ mod \ p \\ a^{2^1} \ mod \ p \\ ... \\ a^{2^{\lfloor log(k) \rfloor}} \ mod \ p a20 mod pa21 mod p...a2⌊log(k)⌋ mod p
预处理的方式为:首先第一项是a,第二项式 a 2 a^2 a2,…,可以发现,后一项是前一项的平方。
- (2)然后我们根据b的二进制表示以及上面对应的结果进行计算。假设 b = 2 x 1 + 2 x 2 + . . . 2 x t b=2^{x_1}+2^{x_2}+...2^{x_t} b=2x1+2x2+...2xt,则:
a b m o d p = a 2 x 1 + 2 x 2 + . . . 2 x t 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 ) a^b \ mod \ p = a ^ {2^{x_1}+2^{x_2}+...2^{x_t}} \ mod \ p \\ = (a ^ {2^{x_1}} \ mod \ p) \times (a ^ {2^{x_2}} \ mod \ p) \times ... \times (a ^ {2^{x_k}} \ mod \ p) ab mod p=a2x1+2x2+...2xt mod p=(a2x1 mod p)×(a2x2 mod p)×...×(a2xk mod p)
- 上面两步的计算都是 O ( l o g ( n ) ) O(log(n)) O(log(n))的,因此次算法的时间复杂度是 O ( l o g ( n ) ) O(log(n)) O(log(n))的。
- 具体代码实现时,上面的二个步骤可以同时计算,代码如下:
typedef long long LL;
// 返回 a ^ b mod p
LL qmi(int a, int b, int p) {
LL res = 1 % p; // 处理b=0, p=1的情况需要%p
while (b) {
if (b & 1) res = res * a % p;
a = (LL)a * a % p;
b >>= 1;
}
return res;
}
快速幂求逆元
-
首先说明一下为什么需要逆元,思想是:将除法转为乘法运算。因为我们认为除法运算太难算了。
-
逆元的定义:若整数
b,m
互质,并且对于任意的整数a
(这里的a
也需要和m
互质,否则后面的推导存在问题,但是不互质等式也能成立),如果满足b|a
,则存在一个整数x
,使得 a b ≡ a × x ( m o d m ) \frac{a}{b} \equiv a \times x \ (mod \ m) ba≡a×x (mod m),则称x
为b
的模m
乘法逆元,记为 b − 1 ( m o d m ) b^{-1} \ (mod \ m) b−1 (mod m)。 -
由定义可知,只有
b
能整除a
且a
也需要和m
互质时,b
才存在逆元。关于a
和m
互质的问题,可以参考如下评论:
b
存在乘法逆元的充要条件是b
与模数m
互质。当模数m
为质数时, b m − 2 b^{m-2} bm−2 即为b
的乘法逆元。证明如下:
a b ≡ a × x ( m o d m ) \frac{a}{b} \equiv a \times x \ (mod \ m) ba≡a×x (mod m)
即:
a
b
≡
a
×
b
−
1
(
m
o
d
m
)
\frac{a}{b} \equiv a \times b^{-1} \ (mod \ m)
ba≡a×b−1 (mod m)
等式的两边同时乘以b
,得到:
a
≡
a
×
b
−
1
×
b
(
m
o
d
m
)
a \equiv a \times b^{-1} \times b \ (mod \ m)
a≡a×b−1×b (mod m)
根据同余的性质,因为a
和m
互质,因此可以两边同时除以a
,得到:
1
≡
b
−
1
×
b
(
m
o
d
m
)
b
−
1
×
b
≡
1
(
m
o
d
m
)
1 \equiv b^{-1} \times b \ (mod \ m) \\ b^{-1} \times b \equiv 1 \ (mod \ m)
1≡b−1×b (mod m)b−1×b≡1 (mod m)
由条件知,m是质数,由费马小定理可知:
b
m
−
1
≡
1
(
m
o
d
m
)
b^{m-1} \equiv 1(mod \ m)
bm−1≡1(mod m)。
因此:b
的逆元为
b
m
−
2
b^{m-2}
bm−2,即
b
−
1
=
b
m
−
2
b^{-1}=b^{m-2}
b−1=bm−2。
- 举个例子,假设
b=3, m=5
,则b
的范围在0~m-1
之间的逆元为 b m − 2 m o d m = 3 3 m o d 5 = 2 b^{m-2} \ mod \ m = 3^3 \ mod \ 5 = 2 bm−2 mod m=33 mod 5=2,取a=6
,则有
6 3 ≡ 6 × 2 ( m o d 5 ) \frac{6}{3} \equiv 6 \times 2 \ (mod \ 5) 36≡6×2 (mod 5)
2. AcWing上的快速幂题目
AcWing 875. 快速幂
问题描述
-
问题链接:AcWing 875. 快速幂
分析
- 使用快速幂求解即可。
代码
- C++
#include <iostream>
using namespace std;
typedef long long LL;
LL qmi(int a, int b, int p) {
LL res = 1 % p; // 处理b=0, p=1的情况需要%p
while (b) {
if (b & 1) res = res * a % p;
a = (LL)a * a % p;
b >>= 1;
}
return res;
}
int main() {
int n;
scanf("%d", &n);
while (n--) {
int a, b, p;
scanf("%d%d%d", &a, &b, &p);
printf("%lld\n", qmi(a, b, p));
}
return 0;
}
AcWing 876. 快速幂求逆元
问题描述
-
问题链接:AcWing 876. 快速幂求逆元
分析
-
根据上面的原理分析可知: a − 1 × a ≡ 1 ( m o d p ) a^{-1} \times a \equiv 1 \ (mod \ p) a−1×a≡1 (mod p),我们要求出
a
的逆元,根据定理知: a − 1 = a p − 2 a^{-1}=a^{p-2} a−1=ap−2。 -
当
a
和p
不互质时,无解;即a
是p
的倍数时无解。 -
因为要返回
0~p-1
之间的逆元,因此最终返回 a p − 2 m o d p a^{p-2} \ mod \ p ap−2 mod p。
代码
- C++
#include <iostream>
using namespace std;
typedef long long LL;
LL qmi(int a, int b, int p) {
LL res = 1 % p;
while (b) {
if (b & 1) res = res * a % p;
a = (LL)a * a % p;
b >>= 1;
}
return res;
}
int main() {
int n;
scanf("%d", &n);
while (n--) {
int a, p;
scanf("%d%d", &a, &p);
if (a % p) printf("%lld\n", qmi(a, p - 2, p));
else puts("impossible"); // 说明a是p的倍数
}
return 0;
}
AcWing 1289. 序列的第k个数
问题描述
-
问题链接:AcWing 1289. 序列的第k个数
分析
-
等差数列:如果首项为 a 1 a_1 a1,公差为
d
,则 a n = a 1 + ( n − 1 ) × d a_n = a_1 + (n - 1) \times d an=a1+(n−1)×d。 -
等比数列:如果首项为 a 1 a_1 a1,公比为
q
,则 a n = a 1 × q n − 1 a_n = a_1 \times q ^ {n - 1} an=a1×qn−1。 -
输入的序列是否可能既是等差数列,又是等比数列呢?这种可能性是存在的,当且仅当输入的前三项全部相等时,才可能既是等差数列,又是等比数列。证明如下:假设前三项是
a、b、c
,则因为既是等差数列,又是等比数列,有:
{ 2 × b = a + c ( 1 ) b 2 = a × c ( 2 ) \begin{cases} 2 \times b = a + c \quad (1) \\ b ^ 2 = a \times c \quad (2) \end{cases} {2×b=a+c(1)b2=a×c(2)
将(1)式中的b
值带入到(2)式,化简可以得到:
(
a
−
c
)
2
=
0
(a - c) ^ 2 = 0
(a−c)2=0,因此a == c
,所以三项都是相等的。
-
因此既是等差数列,又是等比数列,最后第
k
项计算出来的结果是一样的。 -
另外如果这个序列是等比数列,其公比为 q = b a = b ′ a ′ , ( a ′ , b ′ ) = 1 q = \frac{b}{a} = \frac{b'}{a'}, (a', b')=1 q=ab=a′b′,(a′,b′)=1,即
q
的最简分数形式为 b ′ a ′ \frac{b'}{a'} a′b′,则
a n = a 1 × q n − 1 = a 1 × b ′ n − 1 a ′ n − 1 a_n = a_1 \times q ^ {n - 1} = \frac{a_1 \times b ^ {' \ n - 1}}{a ^ {' \ n - 1}} an=a1×qn−1=a′ n−1a1×b′ n−1
因为n
可以任意大,且
a
n
a_n
an是整数,所以a'
必定为1,因此公比q
必定是整数,可以使用快速幂求解
a
n
a_n
an。
代码
- C++
#include <iostream>
using namespace std;
typedef long long LL;
const int mod = 200907;
int qmi(int a, int k) {
int res = 1 % mod;
while (k) {
if (k & 1) res = (LL)res * a % mod;
a = (LL)a * a % mod;
k >>= 1;
}
return res;
}
int main() {
int n;
cin >> n;
while (n--) {
int a, b, c, k;
cin >> a >> b >> c >> k;
if (b * 2 == a + c) cout << (a + (b - a) * (LL)(k - 1)) % mod << endl;
else cout << (a * qmi(b / a, k - 1)) % mod << endl;
}
return 0;
}
AcWing 1290. 越狱
问题描述
-
问题链接:AcWing 1290. 越狱
分析
-
n
个犯人,m
中宗教信仰,问给每个人分配一个宗教信仰,使得存在相邻的人宗教信仰相同,有多少种方案数? -
我们可以求一下一共有多少种分配方式,然后减去不符合题目要求的分配方案,就是结果。
-
一共的分配方案数,因为每个人都可以被分配
m
中宗教信仰中的一个,因此总方案数为: m n m^n mn。 -
不符合题目要求的方案数,即不存在相邻的人宗教信仰相同,则第一个人可以分配
m
中宗教信仰中的一个,后面的人均可以分配m-1
中宗教信仰中的一个(只需要不和前一个相同即可),因此不符合题目要求的方案数为: m × ( m − 1 ) n − 1 m \times (m - 1) ^ {n - 1} m×(m−1)n−1。 -
因此分配方案数为: m n − m × ( m − 1 ) n − 1 m^n - m \times (m - 1) ^ {n - 1} mn−m×(m−1)n−1。
代码
- C++
#include <iostream>
using namespace std;
typedef long long LL;
const int mod = 100003;
int qmi(int a, LL k) {
int res = 1 % mod;
while (k) {
if (k & 1) res = (LL)res * a % mod;
a = (LL)a * a % mod;
k >>= 1;
}
return res;
}
int main() {
LL n; // n个人
int m; // m中宗教信仰
cin >> m >> n;
cout << (qmi(m, n) - (LL)m * qmi(m - 1, n - 1) % mod + mod) % mod << endl;
return 0;
}