矩阵乘法加速递推

当获得一个递推式时,要求我们求第 N N N项,但是这个 N ≤ 2 × 1 0 9 N \le 2×10^9 N2×109很大,单纯的 O ( n ) O(n) O(n)递推无法解决。这个时候就需要用到矩阵乘法来加速递推
矩阵乘法:
A A A n ∗ m n * m nm 矩阵, B B B m ∗ p m * p mp矩阵,则 C = A ∗ B C = A * B C=AB n ∗ p n * p np矩阵,并且 ∀ i ∈ [ 1 , n ] , ∀ j ∈ [ 1 , p ] : C i , j = ∑ k = 1 m A i , k ∗ B k , j \forall i \in [1, n], \forall j \in [1, p]: \\ \quad \\ C_{i,j} = \sum_{k=1}^{m}A_{i,k}*B_{k,j} i[1,n],j[1,p]:Ci,j=k=1mAi,kBk,j

例题1:斐波那契

题目链接

题意:

求斐波那契数列的第 N N N项, N ≤ 2 × 1 0 9 N \le 2 ×10^9 N2×109

Sol:

考虑矩阵加速
构造矩阵 ① ①
( a n a n − 1 a n − 2 ) \begin{pmatrix} a_n&a_{n-1} &a_{n-2}\end{pmatrix}\quad (anan1an2)
我们要得到矩阵 ② ②
( a n + 1 a n a n − 1 ) \begin{pmatrix} a_n+1&a_{n} &a_{n-1}\end{pmatrix}\quad (an+1anan1)
发现需要构造一个 3 × 3 3 × 3 3×3的矩阵,根据矩阵乘法,假设这个矩阵为 ③ ③
( a d g b e h c f i ) \begin{pmatrix} a & d & g \\ b & e & h\\ c & f & i\end{pmatrix}\quad abcdefghi
① × ③ ①×③ ×有:
( a ∗ a n + b ∗ a n − 1 + c ∗ a n − 2 d ∗ a n + e ∗ a n − 1 + f ∗ a n − 2 g ∗ a n + h ∗ a n − 1 + i ∗ a n − 2 ) \begin{pmatrix} a * a_n + b * a_{n-1} + c * a_{n-2} &d * a_n + e * a_{n-1} + f * a_{n-2} & g * a_n + h * a_{n-1} + i * a_{n-2}\end{pmatrix}\quad (aan+ban1+can2dan+ean1+fan2gan+han1+ian2)
在根据递推式: a n = a n − 1 + a n − 2 , a 0 = 0 , a 1 = a 2 = 1 a_n = a_{n-1}+a_{n-2}, a_0 = 0, a_1 = a_2 = 1 an=an1+an2,a0=0,a1=a2=1
可以得出矩阵 ③ ③ 为:
( 1 1 0 1 0 1 0 0 0 ) \begin{pmatrix} 1 & 1 & 0 \\ 1 & 0 & 1\\ 0 & 0 & 0\end{pmatrix}\quad 110100010
当我们要求第 N N N项时,只需求出下式,然后得到的矩阵第一行第一列就是结果: n ≥ 3 ( a 2 a 1 a 0 ) ∗ ( 1 1 0 1 0 1 0 0 0 ) n − 2 n \geq 3 \\ \quad \\ \begin{pmatrix} a_2&a_1 &a_0\end{pmatrix} * \begin{pmatrix} 1 & 1 & 0 \\ 1 & 0 & 1\\ 0 & 0 & 0\end{pmatrix} ^ {n - 2} n3(a2a1a0)110100010n2

Code:
#include <bits/stdc++.h>
using namespace std;
const int mod = 10000;
struct matrix{
    int a[5][5];
}ans, base;

matrix mul1(matrix A, matrix B)
{
    matrix C;
    for(int i = 0; i < 5; ++i)
        for(int j = 0; j < 5; ++j)
            C.a[i][j] = 0;
    for(int i = 1; i <= 1; ++i)
        for(int j = 1; j <= 3; ++j)
        {
            for(int k = 1; k <= 3; ++k)
            {
                C.a[i][j] = (C.a[i][j] + A.a[i][k] * B.a[k][j] % mod) % mod;
                C.a[i][j] %= mod;
            }
        }
    return C;
}
matrix mul2(matrix A, matrix B)
{
    matrix C;
    for(int i = 0; i < 5; ++i)
        for(int j = 0; j < 5; ++j)
            C.a[i][j] = 0;
    for(int i = 1; i <= 3; ++i)
        for(int j = 1; j <= 3; ++j)
        {
            for(int k = 1; k <= 3; ++k)
            {
                C.a[i][j] = (C.a[i][j] + A.a[i][k] * B.a[k][j] % mod) % mod;
                C.a[i][j] %= mod;
            }
        }
    return C;
}

matrix qpow(int b)
{
    matrix res = ans, a = base;
    while(b){
        if(b & 1) res = mul1(res, a);
        a = mul2(a, a);
        b >>= 1;
    }
    return res;
}

int main()
{
    int n;
    while(cin >> n && n != -1)
    {
        if(n == 0) cout << 0 << endl;
        else if(n <= 2) cout << 1 << endl;
        else 
        {
            ans.a[1][1] = 1, ans.a[1][2] = 1, ans.a[1][3] = 0;
            base.a[1][1] = 1, base.a[1][2] = 1, base.a[1][3] = 0;
            base.a[2][1] = 1, base.a[2][2] = 0, base.a[2][3] = 0;
            base.a[3][1] = base.a[3][2] = base.a[3][3] = 0;
            ans = qpow(n - 2);
            cout << ans.a[1][1] << endl;
        }
    }
}

例题2:The Digits String

题目链接

题意:

求:一个长度为 n n n的数字字符串,他的各个数位加起来模4等于0的数量。 1 ≤ n ≤ 1 0 9 1 \le n \le 10^9 1n109

solution

a i a_i ai表示数字 0 ∼ 9 0 \sim 9 09对4为 i i i的数量,那么有: a 0 = 3 , a 1 = 3 , a 2 = 2 , a 3 = 2 a_0 = 3, a_1 = 3, a_2 = 2, a_3 = 2 a0=3,a1=3,a2=2,a3=2
然后用数组 f i , j f_{i,j} fi,j表示长度为 i i i的数字字符串且个数为加起来取模为 j j j的数量 0 ≤ j ≤ 3 0 \le j \le3 0j3,那么容易推出递推式:
f i , 0 = a 0 ∗ f i − 1 , 0 + a 1 ∗ f i − 1 , 3 + a 2 ∗ f i − 1 , 2 + a 3 ∗ f i − 1 , 1 f i , 1 = a 0 ∗ f i − 1 , 1 + a 1 ∗ f i − 1 , 0 + a 2 ∗ f i − 1 , 3 + a 3 ∗ f i − 1 , 2 f i , 2 = a 0 ∗ f i − 1 , 2 + a 1 ∗ f i − 1 , 1 + a 2 ∗ f i − 1 , 0 + a 3 ∗ f i − 1 , 3 f i , 3 = a 0 ∗ f i − 1 , 3 + a 1 ∗ f i − 1 , 2 + a 2 ∗ f i − 1 , 1 + a 3 ∗ f i − 1 , 0 f_{i,0} = a_0 * f_{i-1,0} + a_1 * f_{i - 1,3} + a_2 * f_{i-1,2} + a_3 * f_{i-1,1} \\ f_{i,1}=a_0 * f_{i-1,1} + a_1 * f_{i - 1,0} + a_2 * f_{i-1,3} + a_3 * f_{i-1,2} \\ f_{i,2} = a_0 * f_{i-1,2} + a_1 * f_{i - 1,1} + a_2 * f_{i-1,0} + a_3 * f_{i-1,3} \\ f_{i, 3} = a_0 * f_{i-1,3} + a_1 * f_{i - 1,2} + a_2 * f_{i-1,1} + a_3 * f_{i-1,0} fi,0=a0fi1,0+a1fi1,3+a2fi1,2+a3fi1,1fi,1=a0fi1,1+a1fi1,0+a2fi1,3+a3fi1,2fi,2=a0fi1,2+a1fi1,1+a2fi1,0+a3fi1,3fi,3=a0fi1,3+a1fi1,2+a2fi1,1+a3fi1,0
根据 a i a_i ai写出初始矩阵:
( 3 3 2 2 ) \begin{pmatrix} 3 & 3 & 2 & 2\end{pmatrix}\quad (3322)
根据递推式写出转移矩阵:
( 3 3 2 2 2 3 3 2 2 2 3 3 3 2 2 3 ) \begin{pmatrix} 3 & 3 & 2 & 2 \\ 2 & 3 & 3 & 2\\ 2 & 2 & 3 & 3 \\ 3 & 2 & 2 & 3 \end{pmatrix}\quad 3223332223322233
那么求第 n n n项只需求:
( 3 3 2 2 ) ∗ ( 3 3 2 2 2 3 3 2 2 2 3 3 3 2 2 3 ) n − 1 \begin{pmatrix} 3 & 3 & 2 & 2\end{pmatrix}* \begin{pmatrix} 3 & 3 & 2 & 2 \\ 2 & 3 & 3 & 2\\ 2 & 2 & 3 & 3 \\ 3 & 2 & 2 & 3 \end{pmatrix}^{n-1} (3322)3223332223322233n1

code:

#include <bits/stdc++.h>
using namespace std;

const int mod = 2019;

struct Mat {
    int a[10][10];
};

Mat ans;

Mat mul(Mat A, Mat B) {
    Mat c;
    for(int i = 1; i <= 4; ++ i) {
        for(int j = 1; j <= 4; ++ j)
            c.a[i][j] = 0;
    }
    for(int i = 1; i <= 1; ++ i) {
        for(int j = 1; j <= 4; ++ j) {
            for(int k = 1; k <= 4; ++ k) {
                c.a[i][j] = (c.a[i][j] + A.a[i][k] * B.a[k][j]) % 2019;
            }
        }
    }
    return c;
}
Mat mul2(Mat A, Mat B) {
    Mat c;
    for(int i = 1; i <= 4; ++ i) {
        for(int j = 1; j <= 4; ++ j)
            c.a[i][j] = 0;
    }
    for(int i = 1; i <= 4; ++ i) {
        for(int j = 1; j <= 4; ++ j) {
            for(int k = 1; k <= 4; ++ k) {
                c.a[i][j] = (c.a[i][j] + A.a[i][k] * B.a[k][j]) % 2019;
            }
        }
    }
    return c;
}

Mat qpow(int b) {
    Mat base;
    base.a[1][1] = 3; base.a[1][2] = 3; base.a[1][3] = 2; base.a[1][4] = 2;
    base.a[2][1] = 2; base.a[2][2] = 3; base.a[2][3] = 3; base.a[2][4] = 2;
    base.a[3][1] = 2; base.a[3][2] = 2; base.a[3][3] = 3; base.a[3][4] = 3;
    base.a[4][1] = 3; base.a[4][2] = 2; base.a[4][3] = 2; base.a[4][4] = 3;
    while(b) {
        if(b & 1) ans = mul(ans, base);
        b >>= 1;
        base = mul2(base, base);
    }
    return ans;
}

int main()
{
    int n;
    while(cin >> n) {
        for(int i = 1; i <= 4; ++ i)
            for(int j = 1; j <= 4; ++ j)
                    ans.a[i][j] = 0;
        ans.a[1][1] = 3; ans.a[1][2] = 3; ans.a[1][3] = 2; ans.a[1][4] = 2;
        cout << qpow(n - 1).a[1][1] << endl;
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

W⁡angduoyu

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

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

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

打赏作者

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

抵扣说明:

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

余额充值