一、固定模板
求解 (a / b) % M
主函数中:
ll ans = a * ksm(b, M - 2) % M;
定义快速幂函数:
ll ksm(ll a, ll b){
ll res = 1;
while(b) {
if(b & 1) // 如果当前b的二进制数字为1,即存在
res = res * a % M; // 结果乘上当前a
a = a * a % M; // 下一位的a的值(开平方)
b >>= 1; // b的下一位二进制数
}
return res;
}
二、讲解
对于要求对分数取模的题目,往往不能直接求解,需要用到下面的数学方法。
首先,费马小定理:对于质数p,任意整数a,均满足:
可转化为:
接下来,就是常用数论知识中的快速幂。
我们先看上面转化后的式子,就是快速幂所解决的问题:.
为什么需要快速幂?因为如果朴素做的话需要乘一次a就模一次M,时间复杂度很高。但是快速幂就完美解决了这个问题,将时间由化为了.
从时间复杂度就可以看出来,这肯定需要把幂(b)分解成2的次方形式,我们直接举个例子先帮助理解:
图有些惨不忍睹,不过可以根据下面流程理解:
1.我们要求3的5次方模5,我们先将b二进制化,如文中的b值为5——>101,这样,每个相应的2的n次方相加即为b值。
2.但是呢,这个b值是一个幂,所以将整体相乘就可以实现幂的相加。
这样的话,思路就很明显了,我们需要先将以2的 log b 次方为幂的整体分别预计算出来并模M,最后根据b的二进制相乘再取一次模得出答案。其实就是将原来一个1一个1乘换成了一个二进制数二进制数的乘,在其中分别取模。
还记得dp多重背包的二进制优化吗?异曲同工之妙!
这样再看代码中的注释,就很容易理解了(a不断开平方就是二进制数向前移位的数值整体增加过程)。