营业日志 —— 矩阵加速

发布时间 2024-01-09 00:04:19作者: rzsunly

Q:矩阵加速(数列)

已知一个数列 \(a\),它满足:

\[a_x= \begin{cases} 1 & x \in\{1,2,3\}\\ a_{x-1}+a_{x-3} & x \geq 4 \end{cases} \]

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


我会暴力!

但是时间复杂度 \(O(n)\),数据范围 \(n\leq 2\times 10^9\)

如何优化?

矩阵加速!\(O(\log n)\)


快速幂大家应该都会了吧


前置知识:矩阵

相当于二维数组了(先行后列)

怎么运算呢?

  1. 矩阵加减法:对应位置上的数相加减即可。

  2. 矩阵乘法:\(A\) 的列数 = \(B\) 的行数,

\[C_{i,j}=\displaystyle \sum_{k = 1}^m A_{i,k}\times B_{k,j} \]


如何加速?

关键是先要构造矩阵。

如何构造矩阵?

题解的方法太秒了,直接放在下面了:

我们首先要确定目标矩阵。下面这个矩阵就是我想要的矩阵.

\[\begin{bmatrix}F[i]\\F[i-1]\\F[i-2]\end{bmatrix} \]

那么这个矩阵要怎样算出来。根据题目给出的递推式可以得到下面三个式子

\[f[i] = f[i-1] \times 1 + f[i-2] \times 0 + f[i-3] \times 1 \]

\[f[i-1] = f[i-1] \times 1 + f[i-2] \times 0 + f[i-3] \times 0 \]

\[f[i-2] = f[i-1] \times 0 + f[i-2] \times 1 + f[i-3] \times 0 \]

通过每一项的系数可以得出初始矩阵为

\[\begin{bmatrix}1&0&1\\1&0&0\\0&1&0\end{bmatrix} \]

然后我们就可以通过矩阵快速幂进行求解。

值得注意的是,这个矩阵的 \(N\) 次方算出来的第一个元素是 \(F[N+1]\) ,这样的话我们可以直接在输出的时候输出第二行第一个元素。

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

const int N = 3;
int n, m = 1e9 + 7, T;

void mul(int c[], int a[], int b[][N]) {
  int temp[N] = {0};
  for (int i = 0; i < N; i++)
    for (int j = 0; j < N; j++)
      temp[i] = (temp[i] + (ll)a[j] * b[j][i]) % m;

  memcpy(c, temp, sizeof temp);
}

void mul(int c[][N], int a[][N], int b[][N]) {
  int temp[N][N] = {0};
  for (int i = 0; i < N; i++)
    for (int j = 0; j < N; j++)
      for (int k = 0; k < N; k++)
        temp[i][j] = (temp[i][j] + (ll)a[i][k] * b[k][j]) % m;

  memcpy(c, temp, sizeof temp);
}

int main() { ios::sync_with_stdio(false); cin.tie(0), cout.tie(0);
	cin >> T;
	while (T--) {	
	  int f1[N] = {1, 1, 1}, a[N][N] = {
	    {1, 0, 1},
	    {1, 0, 0},
	    {0, 1, 0}
	  };
		
		cin >> n, n--;
	  while (n) {
	    if (n & 1) mul(f1, f1, a);  // res = res * a
	    mul(a, a, a), n >>= 1;  // a = a * a
	  } // qmi
	
	  cout << f1[1] << '\n';
	}
  return 0;
}