【算法刷题】AcWing 90. 64位整数乘法——位运算

该篇博客介绍了如何处理大数乘法模运算的问题,通过龟速乘算法和二进制技巧降低时间复杂度至O(logb)。博主详细解释了将乘法转换为加法的过程,并提供了C++代码实现,展示了如何避免因数据范围过大导致的long long溢出,从而高效求解a×b mod p的问题。
摘要由CSDN通过智能技术生成

a a a b b b p p p取模的值
输入格式
第一行输入整数 a a a,第二行输入整数 b b b,第三行输入整数 p p p
输出格式
输出一个整数,表示 a × b   m o d   p a \times b\ mod\ p a×b mod p
数据范围
1 ≤ a , b , p ≤ 1 0 18 1 \leq a,b,p \leq 10^{18} 1a,b,p1018
输入样例

3
4
5

输出样例

2

①分析

这题使用到的算法为龟速乘,类似于快速幂,也可以用高精度

a × b   m o d   p 这里相当于  b  个  a  相加  m o d   p ,即用加法来表示乘方 因为数据范围很大,相乘就会爆 l o n g   l o n g ,因而要避免让两个数发生乘法 但如果直接一个一个加,加一次就模上一个 p ,这样时间复杂度为 O ( b ) = 1 0 18 超时,需要降低 1 2 ,故考虑二进制 a   m o d   p 2 a   m o d   p 4 a   m o d   p 8 a   m o d   p … … 2 62 a   m o d   p 发现下一个可以由上一个的二倍得到,类似于二进制 可以将 b 转化为二进制表示,设为 ( 10101 ) 2 , b = 2 0 + 2 2 + 2 4 a × b = a + 4 a + 16 a 因而只需要 a 每次乘以二倍模上 p 迭代,然后看 b 的二进制哪些位上为 1 ,为 1 结果就加上当前 a 的值,为 0 则跳过 复杂度就降为了 O ( l o g b ) a\times b\ mod\ p这里相当于\ b\ 个\ a\ 相加\ mod\ p,即用加法来表示乘方\\ 因为数据范围很大,相乘就会爆long\ long,因而要避免让两个数发生乘法\\ 但如果直接一个一个加,加一次就模上一个p,这样时间复杂度为O(b)=10^{18}超时,需要降低\frac{1}{2},故考虑二进制\\ a\ mod\ p\\ 2a\ mod\ p\\ 4a\ mod\ p\\ 8a\ mod\ p\\ ……\\ 2^{62}a\ mod\ p\\ 发现下一个可以由上一个的二倍得到,类似于二进制\\ 可以将b转化为二进制表示,设为(10101)_2,b=2^0+2^2+2^4\\ a\times b=a+4a+16a\\ 因而只需要a每次乘以二倍模上p迭代,然后看b的二进制哪些位上为1,为1结果就加上当前a的值,为0则跳过\\ 复杂度就降为了O(logb) a×b mod p这里相当于 b  a 相加 mod p,即用加法来表示乘方因为数据范围很大,相乘就会爆long long,因而要避免让两个数发生乘法但如果直接一个一个加,加一次就模上一个p,这样时间复杂度为O(b)=1018超时,需要降低21,故考虑二进制a mod p2a mod p4a mod p8a mod p……262a mod p发现下一个可以由上一个的二倍得到,类似于二进制可以将b转化为二进制表示,设为(10101)2b=20+22+24a×b=a+4a+16a因而只需要a每次乘以二倍模上p迭代,然后看b的二进制哪些位上为1,为1结果就加上当前a的值,为0则跳过复杂度就降为了O(logb)

②代码
#include <cstdio>

typedef long long LL;

LL mul(LL a, LL b, LL p)
{
    LL res = 0;
    while (b)
    {
        if (b & 1)
            res = (res + a) % p;
        a = 2 * a % p;
        b >>= 1;
    }
    
    return res;
}

int main()
{
    LL a, b, p;
    scanf("%lld%lld%lld", &a, &b, &p);
    
    printf("%lld\n", mul(a, b, p));
    
    return 0;
}
③细节分析
LL mul(LL a, LL b, LL p)
{
    LL res = 0;
    while (b)
    {
        if (b & 1) // 判断当前位是否为1
            res = (res + a) % p;
        a = 2 * a % p; // b右移的同时a每次乘2即可得到a、2a...
        b >>= 1;
    }
    
    return res;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ricky_0528

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值