矩阵快速幂加速递推、优化dp

前置知识

快速幂:就是普通快速幂,看完代码其实你会发现矩阵快速幂和普通快速幂基本上没有区别。
线性代数基础:主要就是矩阵乘法的运算,外加一些矩阵乘法的运算律,如结合律,这是满足快速幂的基本条件。
动态规划:没什么可说的。

原理介绍

矩阵快速幂的原理是将矩阵乘法和快速幂相结合。其核心思想是,当需要计算矩阵 A A A n n n 次幂时,借鉴快速幂的思路,把指数 n n n 转化为二进制,然后通过矩阵乘法来计算。
说白了,只要你把矩阵乘法重载一下 ∗ * ,就和普通快速幂没有区别,原理也和普通快速幂类似, 具体代码如下。

代码分析

constexpr int M = 2;\\定义矩阵大小
using Matrix = std::array<std::array<int, M>, M>;\\定义矩阵类型

void init(Matrix &a) {\\初始化为单位矩阵
    for (int i = 0; i < M; i++) {
        a[i][i] = 1;
    }
}

Matrix operator * (const Matrix &a, const Matrix &b) {\\重载MAtrix的乘法运算符
    Matrix ans{};
    for (int i = 0; i < M; i++) {
        for (int j = 0; j < M; j++) {
            for (int k = 0; k < M; k++) {
                ans[i][j] = (ans[i][j] + a[i][k] * b[k][j] % mod) % mod;
            }
        }
    }
    return ans;
}

Matrix power(Matrix a, int b) {\\矩阵快速幂,和普通快速幂基本没有区别
    Matrix ans{};
    init(ans);
    while (b) {
        if (b & 1) {
            ans = ans * a;
        }
        a = a * a;
        b >>= 1;
    }
    return ans;
}

例题分析

1.斐波那契数列

题面很简单,输出斐波那契数列的第 n   ( n < 2 63 ) n \ (n < 2^{63}) n (n<263) 项对 10 9 + 7 10^9 + 7 109+7取模。
这里先给出递推矩阵
[ f ( n ) f ( n − 1 ) ] = [ 1 1 1 0 ] n − 1 × [ f ( 1 ) f ( 0 ) ] \begin{bmatrix} f(n) \\ f(n - 1)\end{bmatrix} = \begin{bmatrix} 1 & 1 \\ 1 & 0 \\ \end{bmatrix} ^ {n - 1}\times\begin{bmatrix}f(1) \\ f(0)\end{bmatrix} [f(n)f(n1)]=[1110]n1×[f(1)f(0)]
根据公式可以直接写出代码。

代码
void solve() {
    int n;
    std::cin >> n;
    Matrix a;
    a[0][0] = 1, a[0][1] = 1, a[1][0] = 1, a[1][1] = 0;
    Matrix ans = power(a, n - 1);
    std::cout << ans[0][0] << '\n';
}

下面,讲解一下这个递推矩阵是怎么来的。
首先先写出第 i i i 项与第 i − 1 i-1 i1 项的关系。
[ f ( n ) ? ] = [ ? ? ? ? ] × [ f ( n − 1 ) ? ] \begin{bmatrix} f(n) \\ ?\end{bmatrix} = \begin{bmatrix} ? & ? \\ ? & ?\end{bmatrix} \times\begin{bmatrix} f(n - 1) \\ ?\end{bmatrix} [f(n)?]=[????]×[f(n1)?]
根据斐波那契数列递推公式可以知道, f ( n ) = f ( n − 1 ) + f ( n − 2 ) f(n) = f(n - 1) + f(n - 2) f(n)=f(n1)+f(n2)
所以可以推出右侧矩阵的 ? ? ? 应为 f ( n − 2 ) f(n - 2) f(n2)
[ f ( n ) ? ] = [ ? ? ? ? ] × [ f ( n − 1 ) f ( n − 2 ) ] \begin{bmatrix} f(n) \\ ?\end{bmatrix} = \begin{bmatrix} ? & ? \\ ? & ?\end{bmatrix} \times\begin{bmatrix} f(n - 1) \\ f(n - 2)\end{bmatrix} [f(n)?]=[????]×[f(n1)f(n2)]
所以左侧矩阵的 ? ? ? f ( n − 1 ) f(n - 1) f(n1)
[ f ( n ) f ( n − 1 ) ] = [ ? ? ? ? ] × [ f ( n − 1 ) f ( n − 2 ) ] \begin{bmatrix} f(n) \\ f(n-1)\end{bmatrix} = \begin{bmatrix} ? & ? \\ ? & ?\end{bmatrix} \times\begin{bmatrix} f(n - 1) \\ f(n - 2)\end{bmatrix} [f(n)f(n1)]=[????]×[f(n1)f(n2)]
我们设递推矩阵为
[ x 1 x 2 x 3 x 4 ] \begin{bmatrix} x_1 & x_2 \\ x_3 & x_4\end{bmatrix} \\ [x1x3x2x4]
可以得到
[ f ( n ) f ( n − 1 ) ] = [ x 1 x 2 x 3 x 4 ] × [ f ( n − 1 ) f ( n − 2 ) ] \begin{bmatrix} f(n) \\ f(n-1)\end{bmatrix} = \begin{bmatrix} x_1 & x_2 \\ x_3 & x_4\end{bmatrix} \times\begin{bmatrix} f(n - 1) \\ f(n - 2)\end{bmatrix} [f(n)f(n1)]=[x1x3x2x4]×[f(n1)f(n2)]
根据矩阵乘法计算可以得到
[ f ( n ) f ( n − 1 ) ] = [ x 1 × f ( n − 1 ) + x 2 × f ( n − 2 ) x 3 × f ( n − 1 ) + x 4 × ( f ( n − 2 ) ] \begin{bmatrix} f(n) \\ f(n-1)\end{bmatrix} = \begin{bmatrix} x_1 \times f(n - 1) + x_2 \times f(n-2) \\ x_3 \times f(n - 1) + x_4 \times(f(n-2)\end{bmatrix} [f(n)f(n1)]=[x1×f(n1)+x2×f(n2)x3×f(n1)+x4×(f(n2)]
很容易得到 x 1 = 1 , x 2 = 1 , x 3 = 1 , x 4 = 0 x_1=1,x_2=1,x_3=1,x_4=0 x1=1,x2=1,x3=1,x4=0
因而得到如下
[ f ( n ) f ( n − 1 ) ] = [ 1 1 1 0 ] × [ f ( n − 1 ) f ( n − 2 ) ]     [ f ( n − 1 ) f ( n − 2 ) ] = [ 1 1 1 0 ] × [ f ( n − 2 ) f ( n − 3 ) ] ⋮ [ f ( 2 ) f ( 1 ) ] = [ 1 1 1 0 ] × [ f ( 1 ) f ( 0 ) ] \begin{bmatrix} f(n) \\ f(n - 1)\end{bmatrix} = \begin{bmatrix} 1 & 1 \\ 1 & 0 \\ \end{bmatrix} \times\begin{bmatrix}f(n - 1) \\ f(n - 2)\end{bmatrix} \\ \ \ \ \\ \begin{bmatrix} f(n - 1) \\ f(n - 2)\end{bmatrix} = \begin{bmatrix} 1 & 1 \\ 1 & 0 \\ \end{bmatrix} \times\begin{bmatrix}f(n - 2) \\ f(n - 3)\end{bmatrix} \\ \vdots\\ \begin{bmatrix} f(2) \\ f(1)\end{bmatrix} = \begin{bmatrix} 1 & 1 \\ 1 & 0 \\ \end{bmatrix} \times\begin{bmatrix}f(1) \\ f(0 )\end{bmatrix} \\ [f(n)f(n1)]=[1110]×[f(n1)f(n2)]   [f(n1)f(n2)]=[1110]×[f(n2)f(n3)][f(2)f(1)]=[1110]×[f(1)f(0)]
所以递推矩阵为
[ f ( n ) f ( n − 1 ) ] = [ 1 1 1 0 ] × [ f ( n − 1 ) f ( n − 2 ) ] = [ 1 1 1 0 ] × [ 1 1 1 0 ] × [ f ( n − 2 ) f ( n − 3 ) ] = ⋯ = [ 1 1 1 0 ] n − 1 × [ f ( 1 ) f ( 0 ) ] \begin{bmatrix} f(n) \\ f(n - 1)\end{bmatrix} = \begin{bmatrix} 1 & 1 \\ 1 & 0 \\ \end{bmatrix} \times\begin{bmatrix}f(n - 1) \\ f(n - 2)\end{bmatrix} = \begin{bmatrix} 1 & 1 \\ 1 & 0 \\ \end{bmatrix} \times \begin{bmatrix} 1 & 1 \\ 1 & 0 \\ \end{bmatrix} \times\begin{bmatrix}f(n - 2) \\ f(n - 3)\end{bmatrix} = \cdots= \begin{bmatrix} 1 & 1 \\ 1 & 0 \\ \end{bmatrix} ^ {n - 1}\times\begin{bmatrix}f(1) \\ f(0)\end{bmatrix} [f(n)f(n1)]=[1110]×[f(n1)f(n2)]=[1110]×[1110]×[f(n2)f(n3)]==[1110]n1×[f(1)f(0)]

2. 矩阵加速(数列)

已知一个数列 a a a,它满足:
a x = { 1 x ∈ { 1 , 2 , 3 } a x − 1 + a x − 3 x ≥ 4 a_x= \begin{cases} 1 & x \in\{1,2,3\}\\ a_{x-1}+a_{x-3} & x \geq 4 \end{cases} ax={1ax1+ax3x{1,2,3}x4

a a a 数列的第 n n n 项对 10 9 + 7 10^9+7 109+7 取余的值。

类似于上面斐波那契数列,依然设递推矩阵
[ f ( n ) f ( n − 1 ) f ( n − 2 ) ] = [ x 1 x 2 x 3 x 4 x 5 x 6 x 7 x 8 x 9 ] × [ f ( n − 1 ) f ( n − 2 ) f ( n − 3 ) ] \begin{bmatrix} f(n) \\ f(n-1) \\ f(n-2)\end{bmatrix} = \begin{bmatrix} x_1 & x_2 & x_3 \\ x_4 & x_5 & x_6 \\ x_7 & x_8 &x_9\end{bmatrix} \times\begin{bmatrix} f(n - 1) \\ f(n - 2) \\ f(n-3)\end{bmatrix} f(n)f(n1)f(n2) = x1x4x7x2x5x8x3x6x9 × f(n1)f(n2)f(n3)
很容易求出
[ x 1 x 2 x 3 x 4 x 5 x 6 x 7 x 8 x 9 ] = [ 1 0 1 1 0 0 0 1 0 ] \begin{bmatrix} x_1 & x_2 & x_3 \\ x_4 & x_5 & x_6 \\ x_7 & x_8 &x_9\end{bmatrix}=\begin{bmatrix} 1 & 0 & 1 \\ 1 & 0 & 0 \\ 0 & 1 & 0\end{bmatrix} x1x4x7x2x5x8x3x6x9 = 110001100

代码(这里换了一个模板)
void solve() {
    int n;
    cin >> n;
    ModMatrix a(3, 3);
    a[0][0] = 1, a[0][2] = 1, a[1][0] = 1, a[2][1] = 1;
    cout << a.pow(n - 1)[0][0] << '\n';
}

3.E2. Trails (Medium)

m m m 个小屋,每个小屋 i i i s i s_i si 条短路、 l i l_i li 条长路连湖边。
每日规则:当前小屋 → → → → 任意小屋(可回原屋),且两条路至少 1 1 1 条是短路。从小屋 1 1 1 出发,连续走 n n n 天,求总路线组合数(对 10 9 + 7 10^9+7 109+7 取模)。
首先需要先分析出转移方程。
f ( i , j ) = ∑ k = 1 m f ( i − 1 , k ) × ( s k ⋅ s j + s k ⋅ l j + l k ⋅ s j ) f(i,j)=\sum^m_{k=1}f(i - 1, k)\times(s_k\cdot s_j +s_k\cdot l_j+l_k\cdot s_j) f(i,j)=k=1mf(i1,k)×(sksj+sklj+lksj)
转移方程有了,就可以设出递推矩阵了,这里假设 m = 3 m=3 m=3
[ f ( n , 1 ) f ( n , 2 ) f ( n , 3 ) ] = [ x 1 , 1 x 1 , 2 x 1 , 3 x 2 , 1 x 2 , 2 x 2 , 3 x 3 , 1 x 3 , 2 x 3 , 3 ] × [ f ( n − 1 , 1 ) f ( n − 1 , 2 ) f ( n − 1 , 3 ) ] \begin{bmatrix} f(n,1) \\ f(n, 2) \\ f(n, 3)\end{bmatrix} = \begin{bmatrix} x_{1, 1} & x_{1, 2} & x_{1,3} \\ x_{2,1} & x_{2,2} & x_{2,3}\\ x_{3,1} & x_{3,2} & x_{3,3} \\ \end{bmatrix} \times\begin{bmatrix}f(n-1, 1) \\ f(n-1,2) \\ f(n-1,3)\end{bmatrix} f(n,1)f(n,2)f(n,3) = x1,1x2,1x3,1x1,2x2,2x3,2x1,3x2,3x3,3 × f(n1,1)f(n1,2)f(n1,3)
剩下的就是解方程了
{ f ( n , 1 ) = x 1 , 1 × f ( n − 1 , 1 ) + x 1 , 2 × f ( n − 1 , 2 ) + x 1 , 3 × f ( n − 1 , 3 ) f ( n , 2 ) = x 2 , 1 × f ( n − 1 , 1 ) + x 2 , 2 × f ( n − 1 , 2 ) + x 2 , 3 × f ( n − 1 , 3 ) f ( n , 2 ) = x 3 , 1 × f ( n − 1 , 1 ) + x 3 , 2 × f ( n − 1 , 2 ) + x 3 , 3 × f ( n − 1 , 3 ) \begin{cases} f(n,1)=x_{1,1}\times f(n-1,1) + x_{1,2}\times f(n-1,2)+x_{1,3} \times f(n-1,3)\\ f(n,2)=x_{2,1}\times f(n-1,1) + x_{2,2}\times f(n-1,2)+x_{2,3} \times f(n-1,3)\\ f(n,2)=x_{3,1}\times f(n-1,1) + x_{3,2}\times f(n-1,2)+x_{3,3} \times f(n-1,3) \end{cases} f(n,1)=x1,1×f(n1,1)+x1,2×f(n1,2)+x1,3×f(n1,3)f(n,2)=x2,1×f(n1,1)+x2,2×f(n1,2)+x2,3×f(n1,3)f(n,2)=x3,1×f(n1,1)+x3,2×f(n1,2)+x3,3×f(n1,3)
不难可以看出 x i , j = s i ⋅ s j + s i ⋅ l j + l i ⋅ s j x_{i,j}=s_i\cdot s_j +s_i \cdot l_j+l_i\cdot s_j xi,j=sisj+silj+lisj

代码
void solve() {
    int m, n;
    cin >> m >> n;
    vector<int> s(m);
    for (int i = 0; i < m; i++) {
        cin >> s[i];
    }
    vector<int> l(m);
    for (int i = 0; i < m; i++) {
        cin >> l[i];
    }
    ModMatrix c(m, m);
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < m; j++) {
            c[i][j] = s[i] * s[j] + s[i] * l[j] + l[i] * s[j];
        }
    }
    auto ans = c.pow(n);
    int res = 0;
    for (int i = 0; i < m; i++) {
        res = (res + ans[0][i]) % mod;
    }
    cout << res << '\n';
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值