快速幂
前言
集训时被递归卡了,非常不牛,于是回来补习了。
1. 原理
快速幂一般用于在 \(\operatorname{log}(k)\) 的时间复杂度下求解 \(a^k\operatorname{mod}p\) 的值。利用到了简单的分治思想。
2. 求法
2.1 递归求
我们发现,当 \(k\) 为奇数时,\(a^k=a^{(k-1)}\times a\)。
当 \(k\) 为偶数时,\(a^k=a^{\frac{k}{2}}\times a^{\frac{k}{2}}\)。
对于 \(k\) 为偶数的情况,只需递归求解出 \(a^{\frac{k}{2}}\) 的值,保存下来,再两个相乘就可以求解出 \(a^k\)。
于是很简单有如下代码:
ll qmi(ll a,ll k){
if(k==0)return 1;
if(k%2!=0){
return (a*qmi(a,k-1)%mod)%mod;
}
else{
ll sub=qmi(a,k/2)%mod;
return (sub*sub)%mod;
}
}
但是递归时会调用堆栈造成资源开销,可能会被卡常,于是有了下面的方法。
2.2 非递归求
我们可以把 \(k\) 写成二进制的形式。
举个例子,当 \(k=15\) 时,\(a^{15}=a^0\times a^1\times a^2\times a^4\times a^8\)
而 \(a^0\) 是 \(1\),且我们发现乘的后一项总是前一项的平方。
于是我们可以从 \(a^0\) 一直递推,如果 \(k\) 的这一位是 \(1\),则乘上此时的 \(a^i\),在递推过程中始终计算 \(\left(a^i\right)^2\)。
ll qfst(ll a,ll k){
ll res=1,t=a;//res 为 a^0,t就是此时的 a^i
while(k){
if(k&1==1){
res=(res*t)%mod;//如果这一位是 1 则乘
}
t=(t*t)%mod;//始终计算平方
k=k>>1;//右移一位,获取到 k 二进制的下一位
}
return res;
}
迁移自洛谷

浙公网安备 33010602011771号