矩阵乘 [Shoi2013]超级跳马

来源:互联网 发布:淘宝买游戏账号被找回 编辑:程序博客网 时间:2024/06/07 01:50

问题 F: [Shoi2013]超级跳马
时间限制: 1 Sec 内存限制: 256 MB
题目描述
现有一个n行m列的棋盘,一只马欲从棋盘的左上角跳到右下角。每一步它向右跳奇数列,且跳到本行或相邻行。跳越期间,马不能离开棋盘。例如,当n = 3, m = 10时,下图是一种可行的跳法。
这里写图片描述
试求跳法种数mod 30011。
输入
仅有一行,包含两个正整数n, m,表示棋盘的规模。
输出
仅有一行,包含一个整数,即跳法种数mod 30011。
样例输入
3 5
样例输出
10
提示
对于100%的数据,1 ≤ n ≤ 50,2 ≤ m ≤ 10^9

看数据范围就知道是矩阵乘。。。
转移是要分奇数列和偶数列的,但实际上并没有那么麻烦。
对于这个方格这里写图片描述
红格的方案应该从所有的黄格转移过来的,可以发现,蓝格其实是其他黄格的前缀和。所以红格方案数=靠近自己的三个黄格方案数+两排前对应那个格的方案数。
因此只要记录两列的方案数即可。转移矩阵我写成了这样,
这里写图片描述
初始矩阵上面n行表示这一列的状态,下面n行表示上一列的状态。
但是还有一个问题。在第三列时,会额外加一次第一行第一列的那个1。而那个1是被我们当作前缀和加上去的。实际上并没有那个前缀和啊,我们并没有跳一步到达1.因此相当于在第三列第一行多了一个1.去掉的方法有很多,大部分都是再乘一边什么的,我yy出了一种方法:在第3列最上多了一个1,跑到第m列产生的方案数,等于从第一列有一个1,跑到第m-2列的方案数。而我们求m列第n行的方案实际上是f[m-1][n]+f[m-1][n-1]+f[m-2][n]的。那么f[m-2][n]恰好就是多出来的方案数。不把他加上去就就成了。

#include <cstdio>#include <cstdlib>#include <cstring>#include <algorithm>#define mod 30011#define ll long longusing namespace std;int n,m,sz;struct node{    ll f[101][101];    node(){memset(f,0,sizeof(f));}    friend node operator *(node a,node b)    {        node c;        for(int i=0;i<sz;i++)            for(int j=0;j<sz;j++)                for(int k=0;k<sz;k++)                    (c.f[i][j]+=a.f[i][k]*b.f[k][j]%mod)%=mod;        return c;    }   }ans,a,b;int main(){    scanf("%d%d",&n,&m);    sz=n*2;m-=2;    a.f[0][0]=1;    for(int i=0;i<n;i++)    {           b.f[i][i+n]=b.f[i+n][i]=1;        if(i!=0)b.f[i][i-1]=1;        if(i!=n-1)b.f[i][i+1]=1;        b.f[i][i]=1;    }    for(int i=0;i<sz;i++)ans.f[i][i]=1;    for(;m;m>>=1,b=b*b)if(m&1)ans=ans*b;    a=ans*a;    printf("%lld\n",(a.f[n-1][0]+a.f[n-2][0])%mod);}