bzoj 4417: [Shoi2013]超级跳马(矩阵合并+快速幂)

本文详细解析了Shoi2013超级跳马问题,通过矩阵快速幂的方法解决了一个马在n行m列的棋盘上从左上角跳到右下角的问题,并给出了具体的算法实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

4417: [Shoi2013]超级跳马

Time Limit: 10 Sec   Memory Limit: 256 MB
Submit: 410   Solved: 252
[ Submit][ Status][ Discuss]

Description

现有一个n行m列的棋盘,一只马欲从棋盘的左上角跳到右下角。每一步它向右跳奇数列,且跳到本行或相邻行。跳越期间,马不能离开棋盘。例如,当n = 3, m = 10时,下图是一种可行的跳法。
 
试求跳法种数mod 30011。

Input

仅有一行,包含两个正整数n, m,表示棋盘的规模。

Output

仅有一行,包含一个整数,即跳法种数mod 30011。

Sample Input

3 5

Sample Output

10


一看n那么小m那么大就可以猜到是一个n*n级别的矩阵自乘m次

但这题还是比较难的,至少比一般的矩阵快速幂难

为方便先调换n和m:

设dp1[x][y]为到达第x行第y列+到达第x-2行第y列+…+到达第1(or2)行第y列的情况总数

dp2[x][y]为到达第x-1行第y列+到达第x-3行第y列+…+到达第2(or1)行第y列的情况总数

如果看不懂就看下面的图吧,假设每个格子上的数字是到达该格子的情况总数

n = 5,m = 5

1  2  3  4  5

1  3  5  7  9

2  4  7  9  9

3  5  6  7  8

1  9  8  9  9

那么dp1[5][4]就是上面红色数字之和,dp2[5][4]和dp1[4][4]就是上面蓝色数字之和

那么可以得到转移:

dp1[x][y] = dp1[x-1][y]+dp1[x-1][y-1]+dp1[x-1][y+1]+dp2[x-1][y]

dp2[x][y] = do1[x-1][y]

最后答案就是dp1[n][m]-dp2[n-1][m]

假设n=3,可以构造出下面矩阵


之后就是愉快的矩阵快速幂了


#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define mod 30011
int n;
typedef struct
{
	int i, j;
	int a[105][105];
	void init()
	{
		memset(a, 0, sizeof(a));
		for(i=1;i<=n/2;i++)
		{
			for(j=1;j<=n/2;j++)
			{
				if(abs(i-j)<=1)
					a[i][j] = 1;
			}
		}
		for(i=n/2+1;i<=n;i++)
			a[i][i-n/2] = a[i-n/2][i] = 1;
	}
	void unit()
	{
		memset(a, 0, sizeof(a));
		for(i=1;i<=n;i++)
			a[i][i] = 1;
	}
}Matrix;
Matrix Powto(Matrix p, int k);
Matrix Jjcf(Matrix p1, Matrix p2);
int main(void)
{
	int m;
	Matrix Jz, A, B;
	scanf("%d%d", &n, &m);
	if(m==1)
	{
		if(n==1)
			printf("1\n");
		else
			printf("0\n");
	}
	else
	{
		n *= 2;
		Jz.init();
		A = Powto(Jz, m-2);
		B = Jjcf(A, Jz);
		printf("%lld\n", (B.a[1][n/2]-A.a[1][n]+mod)%mod);
	}
	return 0;
}

Matrix Powto(Matrix p, int k)
{
	Matrix E;
	E.unit();
	while(k)
	{
		if(k%2)
			E = Jjcf(E, p);
		p = Jjcf(p, p);
		k /= 2;
	}
	return E;
}

Matrix Jjcf(Matrix p1, Matrix p2)
{
	Matrix pe;
	int i, j, k;
	memset(pe.a, 0, sizeof(pe.a));
	for(i=1;i<=n;i++)
	{
		for(j=1;j<=n;j++)
		{
			for(k=1;k<=n;k++)
				pe.a[i][j] = (pe.a[i][j]+p1.a[i][k]*p2.a[k][j])%mod;
		}
	}
	return pe;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值