问题描述
现有一个n行m列的棋盘,一只马欲从棋盘的左上角跳到右下角。每一步它向右跳奇数列,且跳到本行或相邻行。跳越期间,马不能离开棋盘。例如,当n = 3, m = 10时,下图是一种可行的跳法。
试求跳法种数mod 30011。
输入格式
仅有一行,包含两个正整数n, m,表示棋盘的规模。
输出格式
仅有一行,包含一个整数,即跳法种数mod 30011。
样例输入
3 5
样例输出
10
提示
1≤n≤50, 2≤m≤109
思路分析:
前言
题解中各位大佬的方法好像都有点烦诶
窝来一个(个人感觉)比较简单的方法
解法
依旧是考虑dp方程
先考虑一次只能往右跳一格的情况
那么方程很简单
那么如何加上一次往右跳任意奇数格呢?
我们发现除去只能往右跳一格的情况外,所有可以一步跳到(i,j)的都可以一步跳到(i−2,j),所有可以一步跳到(i−2,j)的都可以一步跳到(i,j)
于是,我们的方程就出来了
直接递推的复杂度O(nm),显然不可以接受
于是我们可以采用矩阵优化
接下来,矩阵快速幂就好了qwq
准确代码:
#include<bits/stdc++.h>
using namespace std;
const int mod=30011;int n,m;
struct M{
int w[105][105];
M(){memset(w,0,sizeof(w));}
int *operator[](int a){return w[a];}
M operator*(M&a){
M c;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
for(int k=1;k<=n;k++)
c[i][j]=(c[i][j]+w[i][k]*a[k][j])%mod;return c;
}
}p,x,y;
M pow(M a,int b){
M c;
for(int i=1;i<=n;i++)
c[i][i]=1;
for(;b;b>>=1,a=a*a)if(b&1) c=c*a;return c;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
p[i][i]=p[i+n][i]=p[i][i+n]=1;
}
for(int i=1;i<n;i++)
p[i+1][i]=p[i][i+1]=1;n<<=1,x=pow(p,m-2),y=x*p;
cout<<(y[1][n>>1]-x[1][n]+mod)%mod;
return 0;
}