快速幂
快速幂就是求(a^{b} mod M),如果时间复杂度较低且不需要取模,那就可以直接调用pow(a, b)。
快速幂的时间复杂度是$ O(log b) $。由此可见,这种算法可能运用到了二分或者参数右移啥之类的。这里运用的就是参数b
右移。
对于指数b来说,b可以转变为2的n次方之和。
例如(29 = 2^{4} + 2^{3} + 2^{2} + 2^{0}),对于(a^{29})来说就是
[a^{29} = a^{ 2^{4} + 2^{3} + 2^{2} + 2^{0} } = a^{2^{4}} * a^{2^{3}} * a^{2^{2}} * a^{2^{0}}
]
我再将其转变一下就会成为:
[a^{1 * 2^{4}} * a^{1 * 2^{3}} * a^{1 * 2^{2}} * a^{0 * 2^{1}} * a^{1 * 2^{0}}
]
容易发现2的n次方前的系数,其实就是29的二进制,即为11101,那么求(a^{b} % MOD)就简化成为上面这个的公式了,只需
要表示出这个公式即可,把不断重复发地乘a转变为乘a的2的n次方,时间复杂度自然就降低了。
我们假设 (a^{b}=res) ,那么如果按照传统的一个a一个a的乘的话,那么res的初始值应该为1。此处我们照样是一个一个乘
,但是a的值会不断改变,由于我们此此处的b用的是二进制表示,那么从b的低位开始运算,每次循环的a都是前一次循环a的平
方倍。公式表示就为:(a^{2^{i}} = a^{2^{i - 1}} * a^{2^{i - 1}})。
即下一循环的(a = a * a),只要b在此位为1,那么res就乘以a,否则就进入下一次循环。在这个过程中,将可能超出M的
运算取模就行了。
C++实现代码:
ll ksm(ll a, ll b){
ll res = 1;
while(b) {
if(b & 1) //判断b的二进制在此位是否为1
res = res * a % M;
a = a * a % M; //下一位的a的值
b >>= 1;
}
return res;
}
分数取模
分数取模就是计算 a / b % M, 运用小费马引理
[b^{M−1}modM = 1 mod M
]
转换可得 $$ b^{M−2}modM = b^{-1} mod M $$
$$ a * b^{M−2}modM = a * b^{-1} mod M $$
那么$$ a / b mod M = a * b^{M- 2} mod M$$
所以这里只需结合快速幂函数,直接计算即可.
C++实现代码
ll res = a * ksm(b, M - 2) % M;