洛谷P1226 【模板】快速幂||取余运算

文章介绍了如何使用快速幂算法避免暴力乘法,减少计算次数,特别是处理大数乘法时,通过将指数转换为二进制并逐位处理,结合模运算优化计算过程。同时,为了避免数值过大,采用每次乘法后取模的策略,确保计算的可行性。
摘要由CSDN通过智能技术生成

题面:

解题:

暴力相乘?

Absolutely not!b的范围在(0,)间,倘若我们算阶乘每次只乘一个a,那么一共需要运行b次!

显然,我们并不乐意看到计算机劳神伤身地循环b=-1次,于是,我们应当另辟蹊径。

(注:pow函数同样无法通过。)

快速幂

如何通过最少次的计算得到a的最高次幂?这不禁让我们联想到幂的自乘运算:

* =

* =

* =

……………

于是,对于的运算,我们不需要对ans=1进行13次的*a,

而只需要 * *即可。

对于,我们选择乘了,却没有乘

那么,应该如何判断是否应该乘以呢?

不妨将13转化为二进制数1 1 0 1

观察可以发现,在二进制中,

分别代表8, 4, 2, 1的位上对应的数分别为:11, 0, 1

因此,二进制第n-1位上的数如果是1,则代表需要乘,反之不需要

不妨用数组t[ ]来存放b的二进制数的每一位,len来记录其位数

如下代码即可求得的值:

long long t[10000] = { 0 };
long long a, b, p;
int ans=1;    

//先求出b的二进制数
t[1] = b;
int len = 1; //b二进制数的长度
while (t[len] >= 2) //二进制不允许>=2的数,向后进位
{
    t[len + 1] = t[len] / 2;
    t[len] %= 2;
    len++;
}

long long base = a;
for (int i = 1; i <= len; i++)
{
    if (t[i])  //二进制该位为1
    {
        ans *= base ;
    }
    base *= base;  //自乘
}

取余运算

A×B再取模 = (A取模 × B取模)取模,即:

( A × B ) mod p=[ ( A mod p ) × ( B mod p ) ] mod p

例1:7×7=49 49 % 5=4 等价于 7%5=2 ( 2 * 2 ) % 5 = 4

例2:7×7=49 49 % 9=4 等价于 7%9=7 ( 7 * 7 ) % 9 = 4

对于a和b的数据上限都高达的本题,显然,如果按上述方法计算快速幂,base=a在自乘两次后便可能达到惊人的=!不用高精度的话,我们显然无法储存这样庞大的base。

不过,“上帝给人关上一扇门,也一定会给人打开一扇窗”

以上公式给我们一个有力的启示:对乘数base答案ans都可以: 随 便 Mod

数据庞大?只需要让mod铺天盖地,巨人也无处遁形:

long long base = a;
base %= p;  //base = a本身已经非常庞大了,mod!
for (int i = 1; i <= len; i++)
{
    if (t[i])  //二进制该位为1
    {
        ans = (ans * base) % p; //ans每次累乘后,mod!
    }
    base = (base % p) * (base % p) % p;  //自乘,前mod后mod最后再mod三重保险锁安上
}

AC代码奉上:

#include<iostream>
#include<algorithm>

using namespace std;
long long t[10000] = { 0 };
long long a, b, p;
int ans=1;

int main()
{
    cin >> a >> b >> p;

    //先求出b的二进制表达
    t[1] = b;
    int len = 1;
    while (t[len] >= 2)
    {
        t[len + 1] = t[len] / 2;
        t[len] %= 2;
        len++;
    }

    long long base = a;
    base %= p;
    for (int i = 1; i <= len; i++)
    {
        if (t[i])  //二进制该位为1
        {
            ans = (ans * base) % p;
        }
        base = (base % p) * (base % p) % p;  //自乘
    }

    cout <<a<<"^"<<b<<" mod "<<p<<"="<< ans << endl;

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值