You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4.3 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

##AcWing 875. 快速幂

一、题目描述

给定 na_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的二进制向右移动一位就这样位移、取最低位、位移、取最低位这样循环计算直到指数k0为止,整个过程和我们手动将二进制转换成十进制是非常相似的。

普通幂算法是需要循环指数次,也就是指数是多少就要循环计算多少次,而快速幂因为利用了位移运算,只需要算“指数二进制位的位数”次,对于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位整数乘法)求 abp 取模的值。

输入格式 第一行输入整数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;
}