AcWing 875. 快速幂

\(AcWing\) \(875\). 快速幂

一、题目描述

给定 \(n\)\(a_i,b_i,p_i\),对于每组数据,求出 \(a_i^{b_i}~mod~p_i\) 的值。

输入格式
第一行包含整数 \(n\)

接下来 \(n\) 行,每行包含三个整数 \(a_i,b_i,p_i\)

输出格式
对于每组数据,输出一个结果,表示 \(a^{b_i}~mod~p_i\) 的值。

每个结果占一行。

数据范围
\(1≤n≤100000, 1≤a_i,b_i,p_i≤2×10^9\)

二、快速幂原理

答:通过将指数拆分成几个因数相乘的形式,来简化幂运算。在计算\(3^{13}\) 的时候,普通的幂运算算法需要计算\(13\)次,但是如果我们将它拆分成\(3^{8+4+1}\) ,再进一步拆分成 只需要计算\(4\)次。嗯?哪来的\(4\)次?,别急,接着看。

这种拆分思想其实就是借鉴了二进制与十进制转换的算法思想(倍增),我们知道\(13\)的二进制是\(1101\),可以知道:
\(13=1×2^3 + 1×2^2 + 0×2^1 + 1×2^0 = 8 + 4 + 1\)

原理就是利用位运算里的位移“>>”和按位与“&”运算,代码中\(k\&1\)其实就是取\(k\)二进制的最低位,用来判断最低位是\(0\)还是\(1\),再根据是\(0\)还是\(1\)决定乘不乘,不理解的话联系一下二进制转换的过程。
\(k >>= 1\)其实就是将k的二进制向右移动一位,就这样位移、取最低位、位移、取最低位,这样循环计算,直到指数\(k\)\(0\)为止,整个过程和我们手动将二进制转换成十进制是非常相似的。

普通幂算法是需要循环指数次,也就是指数是多少就要循环计算多少次,而快速幂因为利用了位移运算,只需要算“指数二进制位的位数”次,对于\(13\)来说,二进制是\(1101\),有\(4\)位,就只需要计算\(4\)次,快速幂算法时间复杂度是\(O(logn)\)级别,对于普通幂需要计算一百万次的来说,快速幂只需要计算\(6\)次,这是速度上质的飞跃,但是需要多注意溢出的问题。

三、简单粗暴快速幂

可用于结合高精度乘法

#include <bits/stdc++.h>
using namespace std;

int qmi(int a, int k) {
    int res = 1; 
    while (k) {                    
        if (k & 1) res = res * a;  //假设乘法不会造成溢出
        k >>= 1;                   
        a = a * a;           //假设乘法不会造成溢出       
    }
    return res;
}

int main() {
    printf("%d",qmi(3,4));
    return 0;
}

四、带取模的快速幂

#include <bits/stdc++.h>
using namespace std;
#define int long long

int n;

int qmi(int a, int k, int p) {
    int res = 1;
    while (k) {
        if (k & 1) res = res * a % p;
        k >>= 1;
        a = a * a % p;
    }
    return res;
}

signed main() {
    cin >> n;
    while (n--) {
        int a, k, p;
        cin >> a >> k >> p;
        printf("%d\n", qmi(a, k, p));
    }
    return 0;
}

五、龟速乘

题目描述

\(64\)位整数乘法)求 \(a\)\(b\)\(p\) 取模的值。

输入格式
第一行输入整数\(a\),第二行输入整数\(b\),第三行输入整数\(p\)

输出格式
输出一个整数,表示 \(a*b\) \(mod\) \(p\)的值。

数据范围
\(1 ≤ a , b , p ≤ 10^{18}\)

输入样例

3
4
5

输出样例

2

算法思想

二进制思想。如果直接计算\(a × b\)这会爆 \(long\) \(long\) ,所以采用 类似于快速幂的思想\(b\)作为二进制形式进行处理,然后如果某位上为\(1\)就加上它\(a × 2\)次方,并且每次计算后取模就可以了。

例如:\(b=11=(1011)_2=2^3+2^1+2^0\),那么\(a × b = a × ( 2^3 + 2^1 + 2^0 ) = 8a + 2a + a\)

时间复杂度

将乘数\(b\)的每个二进制位取出进行判断,时间复杂度为\(log(b)\)

代码实现

#include <bits/stdc++.h>
using namespace std;
#define int long long
// 龟速乘
int gui(int a, int b, int p) {
    int res = 0;
    while (b) {
        if (b & 1) res = (res + a) % p;
        a = (a + a) % p;
        b >>= 1;
    }
    return res;
}

int main() {
    int a, b, p;
    cin >> a >> b >> p;
    printf("%lld\n", gui(a, b, p));
    return 0;
}

posted @ 2021-06-29 14:40  糖豆爸爸  阅读(264)  评论(0编辑  收藏  举报
Live2D