NO.67十六届蓝桥杯备战|基础算法-倍增思想|快速幂|快速乘法(C++)
倍增,顾名思义就是翻倍。它能够使线性的处理转化为对数级的处理,极⼤地优化时间复杂度
P1226 【模板】快速幂 - 洛谷
利⽤「⼆进制」以及「倍增」的思想,通过⼀个具体的例⼦说明,⽐如 3 11 3^{11} 311
- 幂运算的运算法则: a b + c = a b × a c a^{b+c}=a^{b} \times a^{c} ab+c=ab×ac
- 通过⼀个数的⼆进制表⽰,可以写成若⼲数相加
11 = ( 1011 ) 2 = 1 × 2 3 + 0 × 2 2 + 1 × 2 1 + 1 × 2 0 11=(1011)_{2}=1 \times 2^{3} +0 \times 2^{2} + 1 \times 2^{1}+1 \times 2^{0} 11=(1011)2=1×23+0×22+1×21+1×20 - 两者结合:
3 11 = 3 ( 1011 ) 2 = 3 1 × 2 3 + 0 × 2 2 + 1 × 2 1 + 1 × 2 0 = 3 8 × 3 0 × 3 2 × 3 1 3^{11}=3^{(1011)_{2}}=3^{1\times 2^{3}+0 \times 2^{2} + 1 \times 2^{1}+1 \times 2^{0}}=3^{8}\times3^{0} \times 3^{2} \times 3^{1} 311=3(1011)2=31×23+0×22+1×21+1×20=38×30×32×31
如何快速算出
3
1
,
3
2
.
.
3
log
2
n
3^{1}, 3^{2} .. 3^{\log_{2}n}
31,32..3log2n
其实很简单,从前往后看,后⼀个数是前⼀个数的平⽅
3
1
=
3
3^{1} = 3
31=3
3
2
=
3
1
×
3
1
=
9
3^{2}=3^{1} \times 3^{1} = 9
32=31×31=9
3
4
=
3
2
×
3
2
=
81
3^{4}=3^{2} \times 3^{2} = 81
34=32×32=81
3
8
=
3
4
×
3
4
=
6561
3^{8}=3^{4} \times 3^{4} = 6561
38=34×34=6561
只需将11的⼆进制表⽰中1 所对应的幂乘起来即可
如何实现这个算法,以 a b a^{b} ab为例
- ⼀边提取b 的⼆进制表⽰中的每⼀位
- ⼀边让a = a ∗ a ,不断变成之前的平⽅(倍增的思想)
- 在提取b 的⼆进制表⽰时,如果这⼀位是1 ,就乘上对应位置的权值
取模运算的规则:
- 在计算过程中,只有加法和乘法时,如果最后要对整个结果取模,取模可以放在任意的位置
- 在计算过程中,存在减法时,结果可能出现负数,此时如果需要补正,就需要模加模的技巧来补正
- 在计算过程中,存在除法的时候,过程中取模是会出现错误的,需要求逆元
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
LL a, b, p;
LL qpow(LL a, LL b, LL p)
{
LL ret = 1;
while (b)
{
if (b & 1) ret = ret * a % p;
a = a * a % p;
b >>= 1;
}
return ret;
}
int main()
{
cin >> a >> b >> p;
printf("%lld^%lld mod %lld=%lld", a, b, p, qpow(a, b, p));
return 0;
}
P10446 64位整数乘法 - 洛谷
跟「快速幂」的思想⼀致,我们通过⼀个具体的例⼦模拟⼀下算法的流程,⽐如3 × 11
- 乘法的分配率: a × ( b + c ) = a × b + a × c a × (b + c) = a × b + a × c a×(b+c)=a×b+a×c ;
- 通过⼀个数的⼆进制表⽰,可以写成若⼲数相加
11 = ( 1011 ) 2 = 1 × 2 3 + 0 × 2 2 + 1 × 2 1 + 1 × 2 0 11 = (1011)_{2}=1 × 2^{3}+ 0 × 2^{2} +1 × 2^{1}+1×2^{0} 11=(1011)2=1×23+0×22+1×21+1×20 - 两者结合:
3 × 11 = 3 × ( 1 × 2 3 + 0 × 2 2 + 1 × 2 1 + 1 × 2 0 ) = 3 × 8 + 3 × 0 + 3 × 2 + 3 × 1 3 × 11=3× (1 × 2^{3}+ 0 × 2^{2} +1 × 2^{1}+1×2^{0})=3 × 8+3×0+3×2+3×1 3×11=3×(1×23+0×22+1×21+1×20)=3×8+3×0+3×2+3×1
如何实现这个算法呢,以a × b 为例
- ⼀边提取b 的⼆进制表⽰中的每⼀位;
- ⼀边让a = a + a ,不断变成之前的两倍(倍增的思想);
- 在提取b 的⼆进制表⽰时,如果这⼀位是1 ,就加上对应位置的权值
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
LL a, b, p;
LL qmul(LL a, LL b, LL p)
{
LL sum = 0;
while (b)
{
if (b & 1) sum = (sum + a) % p;
a = (a + a) % p;
b >>= 1;
}
return sum;
}
int main()
{
cin >> a >> b >> p;
cout << qmul(a, b, p) << endl;
return 0;
}