数论-快速幂、矩阵快速幂

文章目录

  • 快速幂
  • 矩阵快速幂
  • 例题
    • HDU-2817
    • HDU-3117

快速幂

首先幂运算 a n a^n an就是 n n n a a a相乘,我们可以直接调用库函数 p o w ( a , n ) pow(a,n) pow(a,n)来计算,时间复杂度是 O ( n ) O(n) O(n)。不过直接调用往往会超时,还会数据溢出,而快速幂的复杂度是 O ( l o g ( n ) ) O(log(n)) O(log(n)),同时对中间结果进行取模,不会溢出。
快速幂的原理用了位运算。比如把 a 11 a^{11} a11分解成 a 8 、 a 4 、 a 1 a^8、a^4、a^1 a8a4a1的乘积,那么如何求 a 8 、 a 4 、 a 1 a^8、a^4、a^1 a8a4a1?其实不需要分别计算,因为 a 1 × a 1 = a 2 , a 2 × a 2 = a 4 , a 4 × a 4 = a 8 a_1×a_1=a_2,a_2×a_2=a_4,a_4×a_4=a_8 a1×a1=a2,a2×a2=a4,a4×a4=a8都是2的倍数,逐级递推就可以了。
那么怎么分解成11=8+4+1?其实用二进制就一目了然了。1110=10112=23+22+20=8+2+1,按二进制处理,为0的跳过即可。

int fastpow(int a, int n) {
    int res = 1;
    while (n) {
        if (n & 1)  //末位为1
            res = (res * a) % mod;
        a = (a * a) % mod;
        n >>= 1;    //n右移一位
    }
    return res;
}

矩阵快速幂

矩阵幂运算是给定一个m阶矩阵A,求它的n次幂 A n A^n An,复杂度是 O ( m 3 n ) O(m^3n) O(m3n)。矩阵快速幂原理同快速幂一样,只是矩阵乘法无法优化,其复杂度是 O ( m 3 l o g ( n ) ) O(m^3log(n)) O(m3log(n))

struct matrix {
    int a[maxn][maxn];  //矩阵a
    matrix() {  //构造时初始化
        memset(a, 0, sizeof(a));
    }
};
matrix multi(matrix x, matrix y) {  //矩阵乘法
    matrix res;
    for (int i = 0; i < maxn; i++)
        for (int j = 0; j < maxn; j++)
            for (int k = 0; k < maxn; k++)
                res.a[i][j] = (res.a[i][j] + x.a[i][k] * y.a[k][j]) % mod;
    return res;
}
matrix fastm(matrix a, int n) { //矩阵快速幂
    matrix res;
    for (int i = 0; i < maxn; i++)
        res.a[i][i] = 1;    //初始化为单位矩阵
    while (n) {
        if (n & 1)res = multi(res, a);
        a = multi(a, a);
        n >>= 1;
    }
    return res;
}

例题

HDU-2817

HDU-2817 A sequence of numbers

Problem Description
Xinlv wrote some sequences on the paper a long time ago, they might be arithmetic or geometric sequences. The numbers are not very clear now, and only the first three numbers of each sequence are recognizable. Xinlv wants to know some numbers in these sequences, and he needs your help.
Input
The first line contains an integer N, indicting that there are N sequences. Each of the following N lines contain four integers. The first three indicating the first three numbers of the sequence, and the last one is K, indicating that we want to know the K-th numbers of the sequence.
You can assume 0 < K <= 10^9, and the other three numbers are in the range [0, 2^63). All the numbers of the sequences are integers. And the sequences are non-decreasing.
Output
Output one line for each test case, that is, the K-th number module (%) 200907.
Sample Input
2
1 2 3 5
1 2 4 5
Sample Output
5
16

给出序列前3项,要求输出第n项,判断一下等差还是等比,等比的话套快速幂。

#include
using namespace std;
typedef long long ll;
#define mod 200907
ll fastpow(ll a, ll n) {
    ll res = 1;
    while (n) {
        if (n & 1)
            res = (res * a) % mod;
        a = (a * a) % mod;
        n >>= 1;
    }
    return res;
}
int main() {
    ll n, a, b, c, k;
    cin >> n;
    while (n--) {
        cin >> a >> b >> c >> k;
        if (c - b == b - a)
            cout << ((k - 3) * (b - a) + c) % mod << "\n";
        else
            cout << (fastpow(c / b, k - 1) * a) % mod << "\n";
    }
    return 0;
}

HDU-3117

HDU-3117 Fibonacci Numbers

Problem Description
The Fibonacci sequence is the sequence of numbers such that every element is equal to the sum of the two previous elements, except for the first two elements f0 and f1 which are respectively zero and one.
What is the numerical value of the nth Fibonacci number?
Input
For each test case, a line will contain an integer i between 0 and 108 inclusively, for which you must compute the ith Fibonacci number fi. Fibonacci numbers get large pretty quickly, so whenever the answer has more than 8 digits, output only the first and last 4 digits of the answer, separating the two parts with an ellipsis (“…”).
There is no special way to denote the end of the of the input, simply stop when the standard input terminates (after the EOF).
Sample Input
0
1
2
3
4
5
35
36
37
38
39
40
64
65
Sample Output
0
1
1
2
3
5
9227465
14930352
24157817
39088169
63245986
1023…4155
1061…7723
1716…7565

输出第n位斐波那契数列的前四位和后四位,不足8位则直接输出。
首先通项公式 a n = 1 5 [ ( 1 + 5 2 ) n − ( 1 − 5 2 ) n ] a_n=\frac{1}{\sqrt5}[(\frac{1+\sqrt5}{2})^n-(\frac{1-\sqrt5}{2})^n ] an=5 1[(21+5 )n(215 )n],前四位用科学计数法求,并且第40个以前不足8位的直接暴力。
关键是后四位!由递推公式 F ( n ) = F ( n − 1 ) + F ( n − 2 ) F(n)=F(n-1)+F(n-2) F(n)=F(n1)+F(n2)构造矩阵,转换成求这个矩阵的n次幂,再套用矩阵快速幂即可。
[ F ( n + 1 ) F ( n ) F ( n ) F ( n − 1 ) ] = [ 1 1 1 0 ] n \begin{bmatrix} F(n+1) & F(n) \\F(n) & F(n-1)\end{bmatrix}=\begin{bmatrix}1&1\\1 &0\end{bmatrix}^n [F(n+1)F(n)F(n)F(n1)]=[1110]n

#include
using namespace std;
#define mod 10000  //取后四位
const int maxn = 2;  //2阶矩阵
int f[40], n;
struct matrix {
    int a[maxn][maxn];  
    matrix() { 
        memset(a, 0, sizeof(a));
    }
};
matrix multi(matrix x, matrix y) { 
    matrix res;
    for (int i = 0; i < maxn; i++)
        for (int j = 0; j < maxn; j++)
            for (int k = 0; k < maxn; k++)
                res.a[i][j] = (res.a[i][j] + x.a[i][k] * y.a[k][j]) % mod;
    return res;
}
matrix fastm(matrix a, int n) {
    matrix res;
    for (int i = 0; i < maxn; i++)
        res.a[i][i] = 1;   
    while (n) {
        if (n & 1)res = multi(res, a);
        a = multi(a, a);
        n >>= 1;
    }
    return res;
}
void init() {
    f[0] = 0;
    f[1] = 1;
    for (int i = 2; i < 40; i++)
        f[i] = f[i - 1] + f[i - 2];
}
int main() {
    init();
    while (~scanf("%d",&n)) {
        if (n < 40) {
            printf("%d\n", f[n]);
            continue;
        }
        double pre = log10(1.0 / sqrt(5.0)) + (double)n * log10((1.0 + sqrt(5.0)) / 2.0);
        pre = pre - (int)pre;
        printf("%d", (int)(1000.0 * pow(10.0, pre)));
        printf("...");
        matrix last;
        last.a[0][0] = last.a[0][1] = last.a[1][0] = 1;
        last = fastm(last, n);
        printf("%0.4d\n", last.a[0][1]);//注意巨坑:前导0
    }
    return 0;
}

原创不易,请支持正版。(百度发现我的好多博客被抄袭qswl
博主首页:https://blog.csdn.net/qq_45034708
你的点赞将会是我最大的动力,关注一波

你可能感兴趣的:(算法,算法,线性代数)