前言:
求 A * B % C时,当1 <= A,B,C <= 1e18时,A * B 铁定是爆了,比如类似 (1e15 * 1e16 )% 1e17 就有点不好处理了,A * B等于B个A相加,每加一步就取模,就不会出问题了。快速乘类似快速幂都是利用二进制的特性加速这个过程。
快速幂:
快速乘:
乘法分配律:
O(logn) 代码:
ll Fast_mulit(ll a,ll b,ll mod)
{
ll ans = 0; //快速幂这里是1
while(b)
{
if(b&1) ans = (ans+a) % mod;
a = (a+a) % mod; //相当于a*1、a*2、a*4、a*8的增加
b = b>>1;
}
return ans;
}
O(1) :
先看2009国家集训队论文:
骆可强:《论程序底层优化的一些方法与技巧》
再看大佬的解释:
利用
首先 a,b < p 时, a∗b/p 一定小于 p ,我们可以用浮点数进行 a∗b / p 的运算,而不用关心小数点后面的部分。浮点类型long double
在十进制下可以储存 18 19 位。
当浮点数的精度不足以保证位数时,它会以科学记数法舍弃低位,这并不会影响我们需要的整数部分的运算
另外,虽然 a∗b 和 a∗b/p 可能很大,但是二者的差一定在 0 ~ p−1之间,我们只关心它们的较低位数就可以,所以,我们用long long
储存 a∗b 和 [a∗b/p]∗p 各自的结果,整数溢出相当于舍弃最高位,也正好符合我们的要求
代码:
(ps:别人也说了,mod太大了就不建议用这个,个人建议先用O(logn),超时再考虑用这个)
ll Fast_mulit(ll a, ll b, ll p)
{
a %= p, b %= p;
ll c = (long double) a / p *b + 1.0e-8;
ll ans = a * b - c * p;
if (ans < 0) ans += p;
else if (ans >= p) ans -= p;
return ans;
}
经典题目:https://www.luogu.org/problemnew/show/T50035(A^B%C)
(ps:此题的模范围太大就不适合用O(1))
AC代码:
//快速幂+快速乘
#include<iostream>
using namespace std;
typedef unsigned long long ull;
ull b,p,k;
ull Fast_multi(ull a,ull b)
{
ull ans = 0;
while(b)
{
if(b&1) ans = (ans+a)%k;
a = (a+a)%k;
b = b>>1;
}
return ans;
}
ull Fast_pow()
{
b = b%k;
ull ans = 1;
while(p)
{
if(p&1) ans = Fast_multi(ans,b);
b = Fast_multi(b,b);
p = p>>1;
}
return ans % k;
}
int main()
{
while(cin>>b>>p>>k)
cout<<Fast_pow()<<endl;
return 0;
}