O(logn) 与 O(1)的快速乘

前言:

求 A * B % C时,当1 <=  A,B,C <= 1e18时,A * B 铁定是爆了,比如类似 (1e15 * 1e16 )% 1e17 就有点不好处理了,A  * B等于B个A相加,每加一步就取模,就不会出问题了。快速乘类似快速幂都是利用二进制的特性加速这个过程。

快速幂:3^{11}=3^{(1011)_2}=3^{(1000)_2}*3^{(000)_2}*3^{(10)_2}*3^{(1)_2}

快速乘:3*11=3*(1011)_2=3*((1000)_2+(000)_2+(10)_2+(1)_2)

乘法分配律:3*11=3*8*1+3*4*0+3*2*1+3*1*1 

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(mod p)=a*b-[a*b/p]*p
首先 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;
}

猜你喜欢

转载自blog.csdn.net/qq_41157137/article/details/82975298
今日推荐