[SHOI 2013]超级跳马

Posted navi-awson

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[SHOI 2013]超级跳马相关的知识,希望对你有一定的参考价值。

Description

题库连接

现有一个 (n)(m) 列的棋盘,一只马欲从棋盘的左上角跳到右下角。每一步它向右跳奇数列,且跳到本行或相邻行。跳越期间,马不能离开棋盘。试求跳法种数,取模。

(1leq nleq50,2leq mleq 10^9)

Solution

(f_{i,j}) 表示在第 (i) 列第 (j) 行的方案数。那么,(f_{i,j}=sumlimits_{k=1}^i ext{odd}(i-k)cdot(f_{k,j-1}+f_{k,j}+f_{k,j+1}))

显然如果我们分奇偶做一个前缀和的话就可以 (O(1)) 转移。

由于 (n) 比较大,并且决策(转移)比较少,那么我们考虑用矩乘法优化。构造 (1 imes n) 的状态矩阵 (mathbf{A_n}),第 (1) 行第 (i) 列表示从左上角到 ((n,i)) 有多少种方法。

显然 (mathbf{A_n}[1][1]=1)。构造状态矩阵 (mathbf{S}=egin{bmatrix}mathbf{A_n} &mathbf{A_{n-1}}end{bmatrix})

再构造转移矩阵 (mathbf{T}=egin{bmatrix}mathbf{V} &mathbf{I}\mathbf{I} &0end{bmatrix})。其中 (mathbf V) 是转移矩阵,用来记录 DP 的转移方程。右上角的单位矩阵是用来累加前缀和到下一个状态矩阵上的。左下角的单位矩阵是将当前的 (mathbf{A_n}) 变成下一个状态矩阵的 (mathbf{A_{n-1}})

于是矩阵优化即可。注意由于做的是前缀和,所以最后输出时要做一次差分来算出最后一列的方案数。

Code

#include <bits/stdc++.h>
using namespace std;
const int N = 105, yzh = 30011;

int n, m;
struct mat {
    int a[N][N];
    mat () {memset(a, 0, sizeof(a)); }
    mat operator * (const mat &b) const {
        mat ans;
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                for (int k = 1; k <= n; k++)
                    (ans.a[i][j] += a[i][k]*b.a[k][j]%yzh) %= yzh;
        return ans;
    }
} S, T, B;

void qpow(int b) {
    while (b) {
        if (b&1) S = S*T;
        b >>= 1, T = T*T;
    }
}
int main() {
    scanf("%d%d", &n, &m);
    S.a[1][1] = 1;
    for (int i = 1; i <= n; i++)
        T.a[i][i+n] = T.a[i+n][i] = T.a[i][i] = 1;
    for (int i = 1; i < n; i++)
        T.a[i+1][i] = T.a[i][i+1] = 1;
    B = T;  n <<= 1;
    qpow(m-2);
    printf("%d
", ((S*B).a[1][n>>1]-S.a[1][n]+yzh)%yzh);
    return 0;
}

以上是关于[SHOI 2013]超级跳马的主要内容,如果未能解决你的问题,请参考以下文章

[SHOI2013] 超级跳马

[bzoj4417] [Shoi2013] 超级跳马

P3990 [SHOI2013]超级跳马

BZOJ4417: [Shoi2013]超级跳马

bzoj4417 [Shoi2013]超级跳马

bzoj4417[Shoi2013]超级跳马 矩阵乘法