Link
思路分析 本篇题解要求一定线性代数基础。
用一个向量 \(p_i \in \mathbb{N^n}\) 表示从 \((0,0)\) 到第 \(i\) 列的各点的方案数。用一个矩阵 \(D\) 来表示某一列对另一列的贡献: \(p_j \leftarrow p_j + Dp_i\) ,其中 \(j-i\) 为奇数。考虑到马只能上下跳一格,转移矩阵 \(D\) 应该长这样(以 \(n=4\) 为例):
\[ \begin{bmatrix} 1 & 1 & 0 & 0\\ 1 & 1 & 1 & 0\\ 0 & 1 & 1 & 1\\ 0 & 0 & 1 & 1\\ \end{bmatrix} \]
用数学语言描述,就是主对角线以及其上下两条对角线为 \(1\) ,其余为全 \(0\) 。
另外,我们约定仅主对角线全 \(1\) 的单位矩阵为 \(I\) 。
根据上面的分析,我们可以写出向量形式的 dp 转移方程:
\[ p_i=\sum_{k=0}^{\lfloor\frac{i}{2}\rfloor}{Dp_{i-2k-1}} =D\sum_{k=0}^{\lfloor\frac{i}{2}\rfloor}{p_{i-2k-1}} \]
如何优化呢?容易想到根据 \(i\) 的奇偶分别求和。用 \(S_i\) 表示比 \(i\) ,且与 \(i\) 奇偶性相同的所有 \(p_j\) 之和。则有:
\[ S_i=\sum_{k=0}^{\lfloor\frac{i+1}{2}\rfloor}{p_{i-2k}} \]
现在,我们发现:
\[ \begin{aligned} p_i&=DS_{i-1} \\ S_i&=S_{i-2}+p_j \end{aligned} \]
所以有:
\[ S_i=DS_{i-1}+S_{i-2} \]
熟悉的感觉回来了!这玩意就是斐波那契数列的变形!直接上矩阵快速幂优化(没错,用矩阵优化向量转移):
\[ \begin{bmatrix} S_i\\ S_{i-1} \end{bmatrix}=\begin{bmatrix} D & I \\ I & 0 \end{bmatrix}\begin{bmatrix} S_{i-1}\\ S_{i-2} \end{bmatrix}=\begin{bmatrix} D & I \\ I & 0 \end{bmatrix} ^ {i-2}\begin{bmatrix} S_{2}\\ S_{1} \end{bmatrix} \]
虽然 \(D\) 作为矩阵元素本身也是矩阵,但这并不影响优化。当然,矩阵乘法的性质告诉我们将 \(D\) 和 \(p\) 这类矩阵和向量展开成一个个的元素也不影响正确性,但是就显得很繁琐,就不在这里展开了。
AC 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 #include <bits/stdc++.h> using namespace std;const int mod = 30011 ;int n, m;vector<int > dp, zero; vector<vector<int > > trans, ans; vector<vector<int > > multi (const vector<vector<int > > &a, const vector<vector<int > > &b) { vector<vector<int > > res; res.resize (n * 2 , zero); for (int i = 0 ; i < 2 * n; i++) { for (int j = 0 ; j < 2 * n; j++) { for (int k = 0 ; k < 2 * n; k++) { (res[i][j] += a[i][k] * b[k][j]) %= mod; } } } return res; } int main () { scanf ("%d %d" , &n, &m); m -= 2 ; zero.resize (n * 2 , 0 ); dp = zero; dp[0 ] = dp[1 ] = dp[n] = 1 ; trans.resize (n * 2 , zero); ans.resize (n * 2 , zero); for (int i = 0 ; i < n; i++) { if (i - 1 >= 0 ) trans[i][i - 1 ] = 1 ; if (i + 1 < n) trans[i][i + 1 ] = 1 ; trans[i][i] = 1 ; trans[i][i + n] = 1 ; trans[i + n][i] = 1 ; ans[i][i] = ans[i + n][i + n] = 1 ; } while (m) { if (m & 1LL ) ans = multi (ans, trans); trans = multi (trans, trans); m >>= 1 ; } int res = 0 ; for (int i = 0 ; i < n * 2 ; i++) { if (n == 1 ) (res += ans[2 * n - 1 ][i]) %= mod; else (res += (ans[2 * n - 1 ][i] + ans[2 * n - 2 ][i]) * dp[i]) %= mod; } printf ("%d\n" , res); return 0 ; }