[SHOI2013]超级跳马
题目分析
首先,可以先写出一个 \(O(n^3)\) 的暴力DP,这个就不多说了
然后,我们发现 \((i,j-2)\) 是 \((i,j)\) 几乎包含了所有的奇数,因为除了走一步外,其他奇数步实际上都可以到达 \((i,j-2)\)
\(dp[i][j]=dp[i-1][j-1]+dp[i+1][j-1]+dp[i][j-1]+dp[i][j-2]\)
但是,这里的式子不是答案,而是前缀和,于是,最后要只加与终点的值
还是过不了,于是,我们可以想到矩阵快速幂
实际上,初始矩阵记录上一列与上上一列,然后,对于加速矩阵,首先,对角线是有的,然后,当前位上下有值,最后,对应其他列也就是加长度也是有的
最后,多加几个特判
#include <bits/stdc++.h>
using namespace std;
int MOD=30011;
int n,m;
struct Matrix {
int val[105][105];
int n, m;
} A,B;
Matrix cf(Matrix a, Matrix b) {
Matrix ans;
for (int i = 1; i <= 2*n; i++)
for (int j = 1; j <= 2*n; j++) ans.val[i][j] = 0;
for (int i = 1; i <= 2*n; i++) {
for (int j = 1; j <= 2*n; j++) {
for (int k = 1; k <= 2*n; k++) {
ans.val[i][j] += a.val[i][k] * b.val[k][j];
ans.val[i][j] %= MOD;
}
}
}
return ans;
}
Matrix Pow(Matrix temp, int x) {
Matrix ans;
for (int i = 1; i <= 2*n; i++) {
ans.val[i][i] = 1;
}
while (x) {
if (x & 1) {
ans = cf(ans, temp);
}
temp = cf(temp, temp);
x >>= 1;
}
return ans;
}
int main() {
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
{
A.val[i][i]=1;
A.val[i][i-1]=1;
A.val[i][i+n]=1;
A.val[i+n][i]=1;
if(i!=n)
{
A.val[i][i+1]=1;
}
}
B.val[1][1]=1;
B.val[1][2]=1;
B.val[1][1+n]=1;
Matrix rt=Pow(A,m-2);
if(n==1)
{
printf("%d",rt.val[1][1]);
return 0;
}
Matrix ans=cf(B,rt);
if(m<=2)
{
if(n==1||n==2)
{
printf("1\n");
return 0;
}
else
{
printf("0\n");
return 0;
}
}
printf("%d",((ans.val[1][2*n-1]+ans.val[2][2*n-1]+ans.val[1+n][2*n-1])%MOD+((ans.val[1][2*n]+ans.val[2][2*n]+ans.val[1+n][2*n])%MOD))%MOD);
}