快速幂

前言

集训时被递归卡了,非常不牛,于是回来补习了。

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;
}

迁移自洛谷

posted @ 2025-02-04 13:08  hm2ns  阅读(10)  评论(0)    收藏  举报