矩阵快速幂:PIPI的数学题IV

矩阵快速幂:PIPI的数学题IV

问题:

在这里插入图片描述
在这里插入图片描述

思路:

  求斐波拉契数列实际上是一个DP问题,DP的状态转移方程已经给出了,问题在于用给出的状态转移方程进行线性递推,时间复杂度是O(n)的。然而本题的n达到了1e10的规模,肯定会超时,那么有什么办法能优化呢?答案是矩阵快速幂。
  快速幂是什么这里就不多说了,给个传送门:数学专题:同余定理、gcd与lcm、中位数定理、筛法、快速幂等
  那么什么是矩阵快速幂?矩阵快速幂其实就是将DP的状态转移方程通过矩阵相乘的形式表示,再通过快速幂的方法,降低时间复杂度。比如使用矩阵快速幂,该题O(n)的时间复杂度将降至O(logn)
  我们先来看DP[2] = DP[1] + DP[0],我们可以构造这样一个矩阵相乘来求出DP[2]:
在这里插入图片描述
  那么如何求出DP[3],再乘以一个我们所构造的矩阵即可:
在这里插入图片描述
  依次类推,我们可以发现规律如下:
在这里插入图片描述
  于是,对于求DP[n],我们就可以不使用线性递推,而是通过矩阵幂的方法求出。根据快速幂算法,只需要做logn次矩阵乘法就可以计算出第二个矩阵的n-1次幂是多少。而这是个2*2的矩阵,每做一次矩阵乘法的复杂度为一个常数。因此使用矩阵快速幂就可以把时间复杂度从O(n)优化成O(logn)。

  一般矩阵快速幂都是和DP联系在一起的,当找到DP状态转移方程,但规模很大的话,可以考虑构造矩阵快速幂进行优化

代码:

import java.util.*;

public class Main {
    static long[][] startMatrix = new long[2][2];
    static long[][] mulMatrix = new long[2][2];
    static final long MOD_NUM = (long) (1e9 + 7);
    public static void main(String[] args) {
        startMatrix[0][0] = 1;
        startMatrix[0][1] = 1;
        mulMatrix[0][0] = 1;
        mulMatrix[0][1] = 1;
        mulMatrix[1][0] = 1;
        long k;
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNextLong()) {
            k = scanner.nextLong();
            System.out.println(fastMatrixPow(startMatrix, k - 1)[0][0] % MOD_NUM);
        }

    }

    static long[][] fastMatrixPow(long[][] matrix, long powNum) {
        long[][] baseMatrix = mulMatrix;
        while (powNum != 0) {
            if ((powNum & 1) == 1) {
                matrix = mulMatrix(matrix, baseMatrix);
            }
            baseMatrix = mulMatrix(baseMatrix, baseMatrix);
            powNum >>= 1;
        }
        return matrix;
    }

    static long[][] mulMatrix(long[][] matrix1, long[][] matrix2) {
        long[][] ans = new long[2][2];
        ans[0][0] = (matrix1[0][0] * matrix2[0][0] % MOD_NUM)
                    + (matrix1[0][1] * matrix2[1][0] % MOD_NUM);
        ans[0][1] = (matrix1[0][0] * matrix2[0][1] % MOD_NUM)
                + (matrix1[0][1] * matrix2[1][1] % MOD_NUM);
        ans[1][0] = (matrix1[1][0] * matrix2[0][0] % MOD_NUM)
                + (matrix1[1][1] * matrix2[1][0] % MOD_NUM);
        ans[1][1] = (matrix1[1][0] * matrix2[0][1] % MOD_NUM)
                + (matrix1[1][1] * matrix2[1][1] % MOD_NUM);
        return ans;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

happy19991001

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

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

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

打赏作者

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

抵扣说明:

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

余额充值