二分快速幂和快读快写模板【C++】

二分快速幂与快读

二分快速幂

问题引入

现在要求计算 aⁿ % p 的值,其中a , n , p为整数,且0 < a, p < 10⁹ , 0 < n < 10¹⁸

我们朴素的想法一般都会想到暴力求解,直接循环计算aⁿ,最后再对p取模

long long ans = 1;
for(int i = 1 ; i <= p ; i++)
ans *= n;
ans %= p;
return ans;

这样有两个问题

  1. 其一:很可能溢出long long 的范围

  2. 其二:时间复杂度太高,进行10¹⁸次的运算,已经超出大部分计算机的能力

这里我们就要引入一种快速计算幂运算的算法--二分快速幂

首先我们要明确取模运算的性质

(a * b) % p = [(a % p) * (b % p)] % p

(用数论来理解其实就是证明a * b 和 [(a % p) * (b % p)]同余)

证明

证明如下:

设 a = k1 * p + q1 , b = k2 * p + q2

a ^ b = C * p + q1 * q2

(a * b) % p = q1 * q2

(a % p) * (b % p) = q1 * q2

证毕

那么这和我们的二分快速幂又有什么关系呢?

假如我们现在要手算3¹⁰

我们可以转化为 9⁵ ,再转化为9 * 81² ,再转化为 9 * (81²)¹ (81²的计算交给计算机就好)

如果我们要求在p范围内,我们每次转化模上p就能保证结果的正确性

那么代码如何实现呢

inline long long quickPower(int a, int b, int p)
{
    long long ans = 1;
    while (b)
    {
        if (b & 1)
        {
            ans = ans * a % p;
        }
        a = a * a % p;
        b /= 2;
    }
    return ans;
}

快读

在IO型的OJ题中,我们经常要进行数据读入,如果数据量特别大,我们传统的数据读取就会消耗大量的时间

对C++来说,cin不需要格式化控制,非常方便,但是它的效率其实是低于我们C语言中的scanf的

但是就算我们用scanf来代替cin,在面对较多的数据时,也不是一种很好的方法

这个时候我们就要进行优化(快读针对的是int类型)

我们数据方式的效率从低到高为

cin < scanf < getchar < fread

这样我们可以利用getchar或者fread针对int类型进行一种优化

getchar版本

inline int read()
{
    int s = 0, w = 1;//s 数值 w符号
    char ch = getchar();
    while (ch < '0' || ch > '9')
    {
        if (ch == '-')
            w *= -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
    {
        s = (s << 1) + (s << 3) + (ch ^ 48);
//该行代码的解释为  数字字符串转为对应的数字大小 而我们可以用位运算来进行优化
//一般我们写为 s = s * 10 + ch - 48
//而s * 10 = s << 1 + s << 3   
//ch - 48 由于48二进制为110000 57 为 111001 ch - 48的值就相当于与48做异或
        ch = getchar();
    }
    return s * w;
}

其实getchar已经很快了,但是我们还可以利用fread进一步优化

注意:fread在某些平台环境下遇到读取stdin遇到换行符不会停止,最好用第一种

代码如下:

char buf[1 << 21], * p1 = buf, * p2 = buf;
inline char gc()
{
    if (p1 == p2)//p1 == p2说明数据流中的数据读完了,可以进行下一次读取了
    {
        int cnt = fread(buf, sizeof(char), 1 << 21, stdin);
        
        p2 = (p1 = buf) + cnt;
    }
    return *(p1++);
}
​
​
inline int read()
{
    int s = 0, w = 1;//s 数值 w符号
    char ch = gc();
    while (ch < '0' || ch > '9')
    {
        if (ch == '-')
            w *= -1;
        ch = gc();
    }
    while (ch >= '0' && ch <= '9')
    {
        s = (s << 1) + (s << 3) + (ch ^ 48);
        ch = gc();
    }
    return s * w;
}

快写

既然有快读,当然也有快写,同样是按位处理。

inline void write(int x)
{
    if (x < 0)
        putchar('-'), x = -x;

    if (x > 9)
        write(x / 10);
    putchar((x % 10) ^ 48);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

EQUINOX1

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

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

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

打赏作者

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

抵扣说明:

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

余额充值