矩阵乘 [Shoi2013]超级跳马

该博客探讨了[Shoi2013]超级跳马问题的解决方法,涉及棋盘上马的跳跃路径计数。通过矩阵乘法,博主解释了如何计算在给定的n行m列棋盘上,马从左上角跳到右下角的可行路径数,并对转移矩阵进行了详细阐述。特别地,讨论了奇数列和偶数列的处理,以及在第三列时避免多加一次方案数的问题。
摘要由CSDN通过智能技术生成

问题 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 long
using 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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值