MorsLin 的博客

MorsLin 的博客

前事不忘,后事之师

题解 P3390 【【模板】矩阵快速幂】

posted on 2018-03-07 08:48:16 | under 题解 |

矩阵运算法则

  • 矩阵 A 的大小为 $n × m$, B 的大小为 $n × k$,设$C = A × B$
    则$C_{i,j}=\sum\limits^n_{k=1}A_{i,k}\times B_{k,j}$
    例如:
    $\begin{bmatrix}2 & 1\\4 & 3\end{bmatrix} \times \begin{bmatrix}1 & 2\\1 & 0\end{bmatrix} = \begin{bmatrix}2 \times 1 + 1 \times 1 & 2 \times 2 + 1 \times 0 \\4 \times 1 + 3 \times 1 & 4\times 2 + 3\times 0 \end{bmatrix} = \begin{bmatrix}3 & 4\\7 & 8\end{bmatrix}$

  • 矩阵乘满足结合律:$(AB)C = A(BC)$

  • 有一种特殊的矩阵:单位矩阵,它从左上角到右下角的对角线上的元素均为1,其他元素均为0。它在矩阵乘中相当于数乘中的1,即任何矩阵乘它都等于本身。
    例:
    $e = \begin{bmatrix}1 & 0\\0 & 1\end{bmatrix}$

以上这些就是学会矩阵快速幂前必备的基础知识。

代码实现

  • 理解了矩阵乘法之后,我们可以用函数来模拟矩阵乘。

    Mat Mul(Mat x, Mat y) //矩阵乘
    {
        Mat c;
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                c.m[i][j] = 0;
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                for (int k = 1; k <= n; k++)
                    c.m[i][j] = (c.m[i][j] + x.m[i][k] * y.m[k][j] % mod) % mod;
        return c;
    }
  • 因为矩阵乘满足结合律,所以快速幂完全适用于矩阵,矩阵快速幂和普通快速幂几乎一模一样,不同点在于乘号改成了 Mul 函数(不会普通快速幂的请前往P1226

    Mat pow(Mat x, LL y) //矩阵快速幂
    {
        Mat ans = e;
        while (y)
        {
            if (y & 1)
                ans = Mul(ans, x);
            x = Mul(x, x);
            y >>= 1;
        }
        return ans;
    }

知道这些知识后,这道题基本就可以 AC 了。要注意开 long long,不然会爆零。

AC代码:

#include <iostream>
#include <cstring>
#define mod 1000000007
#define LL long long
using namespace std;
struct Mat
{
    LL m[101][101];
};        //结构体存矩阵
Mat a, e; //a是输入的矩阵,e是单位矩阵
LL n, p;
Mat Mul(Mat x, Mat y) //矩阵乘
{
    Mat c;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            c.m[i][j] = 0;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            for (int k = 1; k <= n; k++)
                c.m[i][j] = (c.m[i][j] + x.m[i][k] * y.m[k][j] % mod) % mod;
    return c;
}
Mat pow(Mat x, LL y) //矩阵快速幂
{
    Mat ans = e;
    while (y)
    {
        if (y & 1)
            ans = Mul(ans, x);
        x = Mul(x, x);
        y >>= 1;
    }
    return ans;
}
int main()
{
    //输入
    cin >> n >> p;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            cin >> a.m[i][j];
    //算法核心
    for (int i = 1; i <= n; i++)
        e.m[i][i] = 1;
    Mat ans = pow(a, p);
    //输出
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= n; j++)
            cout << ans.m[i][j] % mod << " ";
        cout << endl;
    }
    return 0;
}

P.S. 关于用结构体的好处:向函数中传参和变量之间赋值比较方便。直接用二维数组当然也没什么问题