[C++]矩阵加速

文章目录

  • 正题:矩阵加速
    • 举个例子
      • A矩阵
      • 转移
      • B矩阵
      • 终极时刻——代码时间

首先,先了解一下常识

正题:矩阵加速

众所周知,矩阵加速是非常神奇的
然而,怎么思考转移却成了难题。
首先,我们得知道,要是用矩阵加速, 那么对于矩阵 C,一定可以通过 A矩阵 乘以 B n B^n Bn(B为矩阵)
这样,我们就可以通过快速幂求解
然而,A,B矩阵怎么定义?容易想到,在 A n A_n An 矩阵中,如果他的转移用到了 A n A_n An 矩阵中任意一个元素的第 n 号元素,一般是推不出来的

举个例子

看题
这道题,需要我们拿出草稿纸,对吧?

A矩阵

根据经验,我们的 A 矩阵需要存 T n T_n Tn, f n f_n fn f n − 1 f_n - 1 fn1,然而我万万没有想到我们竟然还需要存 f n f_n fn × \times × n, f n − 1 f_n - 1 fn1 × \times × (n - 1)(因为这个常数项,显然是有规律可循的,但我恰恰忘了,矩阵乘法之所以可以加速,就是运用了这些规律哇)(还有,以后如 f n f_n fn × \times × n n n 这种记为 G n G_n Gn
总而言之,A矩阵 = [ T n f n f n − 1 G n G n − 1 ] \begin{bmatrix} T_n & f_n & f_{n - 1} & G_n & G_{n-1}\end{bmatrix} [Tnfnfn1GnGn1]

转移

这时,我们就要思考怎么由 A 到 C,或者说,怎么由 T n − 1 T_{n - 1} Tn1 T n T_n Tn
这里有个简单的办法,把 T n T_n Tn 拆掉
T n T_n Tn = T n − 1 T_{n - 1} Tn1 + + + G n G_n Gn
出现了 第 n 项,继续化,将 G n G_n Gn 提出,单独拆
G n G_n Gn = f n f_n fn × \times × n n n,及 f n f_n fn = f n − 1 f_{n - 1} fn1 + f n − 2 f_{n - 2} fn2
G n G_n Gn = G n − 1 G_{n - 1} Gn1 + + + f n − 1 f_{n - 1} fn1 + G n − 2 G_{n - 2} Gn2 + 2 2 2 × \times × f n − 2 f_{n - 2} fn2

B矩阵

现在算出了关于 T n T_n Tn 的转移方式,接下来就是构造 B矩阵了(说了,这么久终于到了重点)
提供一种方法(非常不实用,换言之就是暴力),根据矩阵乘法的知识,我们可以知道C矩阵一定是以A矩阵的行数为行数,以B矩阵的列数为列数的矩阵,现在我们想要构造一个 1 × \times × 5 的C矩阵,且我们知道 B矩阵的行数为 A 矩阵(1 × \times × 5)的列数,所以可以知道 B 矩阵是一个 5 × \times × 5 的矩阵,所以我们可以假设
B矩阵 = [ a 11 a 12 a 13 a 14 a 15 a 21 a 22 a 23 a 24 a 25 a 31 a 32 a 33 a 34 a 35 a 41 a 42 a 43 a 44 a 45 a 51 a 52 a 53 a 54 a 55 ] \begin{bmatrix} a11 & a12 & a13 & a14 & a15\\ a21 & a22 & a23 & a24 & a25\\ a31 & a32 & a33 & a34 & a35\\ a41 & a42 & a43 & a44 & a45\\ a51 & a52 & a53 & a54 & a55 \end{bmatrix} a11a21a31a41a51a12a22a32a42a52a13a23a33a43a53a14a24a34a44a54a15a25a35a45a55
然后,把 A,B矩阵相乘的乘积列出来,看看哪些元素是我们需要的,需要几个(这个 a i j a_{ij} aij为常数),哪些元素是我们所不需要(此时 a i j a_{ij} aij = 0 0 0
(由于第一次使用Markdown所以我就不全都列出来,只举其中一个例子)
T n T_n Tn = T n − 1 T_{n - 1} Tn1 × \times × a 11 a_{11} a11 + + + f n − 1 f_{n - 1} fn1 × \times × a 21 a_{21} a21 + + + f n − 2 f_{n - 2} fn2 × \times × a 31 a_{31} a31 + + + G n − 1 G_{n - 1} Gn1 × \times × a 41 a_{41} a41 + + + G n − 2 G_{n - 2} Gn2 × \times × a 51 a_{51} a51
根据 T n T_n Tn = T n − 1 T_{n - 1} Tn1 + + + G n − 1 G_{n - 1} Gn1 + + + f n − 1 f_{n - 1} fn1 + G n − 2 G_{n - 2} Gn2 + 2 2 2 × \times × f n − 2 f_{n - 2} fn2,我们可知 a 11 a_{11} a11 = 1, a 21 a_{21} a21 = 1, a 31 a_{31} a31 = 2, a 41 a_{41} a41 = 1, a 51 a_{51} a51 = 1
照此方法,即可推出
B = [ 1 0 0 0 0 2 0 1 0 2 1 1 1 0 1 1 0 0 0 1 1 0 0 1 1 ] \begin{bmatrix} 1 & 0 & 0 & 0 & 0\\ 2 & 0 & 1 & 0 & 2\\ 1 & 1 & 1 & 0 & 1\\ 1 & 0 & 0 & 0 & 1\\ 1 & 0 & 0 & 1 & 1 \end{bmatrix} 1211100100011000000102111
(注明一点,由于作者本人比较懒,所以就没有重新推,此 B 矩阵对应的并非上面推出的 A 矩阵(对应元素位置换了))
A = [ T n f n − 1 f n G n − 1 G n ] \begin{bmatrix} T_n & f_{n - 1} & f_n & G_{n - 1} & G_n \end{bmatrix} [Tnfn1fnGn1Gn]
(没骗你们吧)

终极时刻——代码时间

万事俱备,只欠代码

#include
#include
#define M 6
#define reg register
typedef long long LL;
const int P = 1000000007;
using namespace std;

int n;

struct Matrix{
    int n,m;
    LL c[M][M];
    Matrix(){ memset(c,0,sizeof(c)); }
    Matrix operator * (const Matrix & rhs){
        Matrix ans;
        ans.n = n;
        ans.m = rhs.m;
        for (reg int i = 1;i <= ans.n; ++ i)
            for (reg int j = 1;j <= ans.m; ++ j)
                for (reg int k = 1;k <= m; ++ k)
                    ans.c[i][j] = (ans.c[i][j] + c[i][k] * rhs.c[k][j] % P) % P;
        return ans;
    }
}A,B;

LL qkpow(Matrix D,int y){
    while (y){
        if (y & 1)
            A = A * D;
        D = D * D;
        y >>= 1;
    }
}


int main(){
    ios::sync_with_stdio(false);
    cin.tie(); cout.tie();
    cin >> n;
    if (n == 1){
        cout << 1 << endl;
        return 0;
    }
    A.n = 1,A.m = 5;
    B.n = B.m = 5;
    A.c[1][1] = 3,A.c[1][2] = A.c[1][3] = A.c[1][4] = 1,A.c[1][5] = 2;
    B.c[1][1] = B.c[2][3] = B.c[3][1] = B.c[3][2] = B.c[3][3] = B.c[3][5] = B.c[4][1] = B.c[4][5] = B.c[5][1] = B.c[5][4] = B.c[5][5] = 1;
    B.c[2][1] = B.c[2][5] = 2;
    qkpow(B,n - 2);
    cout << A.c[1][1] << endl;
    return 0;
}

你可能感兴趣的:(数论,矩阵加速)