bzoj4417 超级跳马 矩阵乘法

       一道不算太裸的矩阵乘法。

       考虑对奇数和偶数分开来计算,令dp1[i][j]表示到第2i-1列在第j行的方案,dp2[i][j]表示到第2i列第j行的方案,那么显然有dp1[i][j]=Σdp2[1..i-1][j],dp2类似。考虑做一个令f[i][j]为dp[1..i][j]的前缀和,那么就有f1[i][j]=f1[i-1][j]+f2[i][j],f2[i][j]=f2[i-1][j]+f1[i][j]。

       那么对后面那个就是一个简单的递推式了,可以用矩阵乘法加速。由于有两个数组,我们把它们合成一个用i表示f1中的i,i+1表示f2中的i,然后就可以两行两行计算了。注意对m的奇偶性进行判断,因为初值是不同的。

AC代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#define p 30011
using namespace std;

int n,m,cnt; struct matrix{ int t[105][105]; }a,b,c;
matrix tms(matrix x,matrix y){
	matrix z; int i,j,k;
	for (i=1; i<=cnt; i++)
		for (j=1; j<=cnt; j++){
			z.t[i][j]=0;
			for (k=1; k<=cnt; k++) z.t[i][j]=(z.t[i][j]+x.t[i][k]*y.t[k][j])%p;
		}
	return z;
}
int main(){
	scanf("%d%d",&n,&m); cnt=n<<1; int i,k;
	m--; if (m==1){ puts((n<3)?"1":"0"); return 0; }
	for (i=1; i<=n; i++){
		a.t[i][i]=a.t[i+n][i+n]=b.t[i][i]=b.t[i+n][i+n]=1;
		for (k=i-1; k<=i+1; k++)
			if (k>0 && k<=n) a.t[i][k+n]=b.t[i+n][k]=1;
	}
	c.t[1][1]=1; if (m&1){ c.t[1][2]=c.t[1][n+1]=1; }
	m>>=1; a=tms(a,b); memcpy(b.t,a.t,sizeof(a.t));
	for (i=m-1; i; i>>=1,a=tms(a,a)) if (i&1) c=tms(c,a);
	printf("%d\n",(tms(c,b).t[1][n]-c.t[1][n]+p)%p);
	return 0;
}


by lych

2016.3.9

你可能感兴趣的:(快速幂,递推,矩阵乘法)