[SHOI2013]超级跳马

题目分析

首先,可以先写出一个 \(O(n^3)\) 的暴力DP,这个就不多说了

然后,我们发现 \((i,j-2)\)\((i,j)\) 几乎包含了所有的奇数,因为除了走一步外,其他奇数步实际上都可以到达 \((i,j-2)\)

\(dp[i][j]=dp[i-1][j-1]+dp[i+1][j-1]+dp[i][j-1]+dp[i][j-2]\)

但是,这里的式子不是答案,而是前缀和,于是,最后要只加与终点的值

还是过不了,于是,我们可以想到矩阵快速幂

实际上,初始矩阵记录上一列与上上一列,然后,对于加速矩阵,首先,对角线是有的,然后,当前位上下有值,最后,对应其他列也就是加长度也是有的

最后,多加几个特判

#include <bits/stdc++.h>
using namespace std;
int MOD=30011;
int n,m;
struct Matrix {
    int val[105][105];
    int n, m;
} A,B;
Matrix cf(Matrix a, Matrix b) {
    Matrix ans;
    for (int i = 1; i <= 2*n; i++)
        for (int j = 1; j <= 2*n; j++) ans.val[i][j] = 0;
    for (int i = 1; i <= 2*n; i++) {
        for (int j = 1; j <= 2*n; j++) {
            for (int k = 1; k <= 2*n; k++) {
                ans.val[i][j] += a.val[i][k] * b.val[k][j];
                ans.val[i][j] %= MOD;
            }
        }
    }
    return ans;
}
Matrix Pow(Matrix temp, int x) {
    Matrix ans;
    for (int i = 1; i <= 2*n; i++) {
        ans.val[i][i] = 1;
    }
    while (x) {
        if (x & 1) {
            ans = cf(ans, temp);
        }
        temp = cf(temp, temp);
        x >>= 1;
    }
    return ans;
}
int main() {
	scanf("%d %d",&n,&m);
	
	 for(int i=1;i<=n;i++)
	 {
	 	A.val[i][i]=1;
	 	A.val[i][i-1]=1;
	 	A.val[i][i+n]=1;
	 	A.val[i+n][i]=1;
	 	if(i!=n)
	 	{
	 		A.val[i][i+1]=1;
		 }
	 }
	 B.val[1][1]=1;
	 B.val[1][2]=1;
	 B.val[1][1+n]=1;
	 Matrix rt=Pow(A,m-2);
	 if(n==1)
	 {
	 	printf("%d",rt.val[1][1]);
	 	return 0;
	  } 
	 Matrix ans=cf(B,rt);
	 
	  if(m<=2)
	  {
	  	if(n==1||n==2)
	  	{
	  		printf("1\n");
	  		return 0;
		  }
		  else
		  {
		  	printf("0\n");
		  	return 0;
		  }
	  }
	  printf("%d",((ans.val[1][2*n-1]+ans.val[2][2*n-1]+ans.val[1+n][2*n-1])%MOD+((ans.val[1][2*n]+ans.val[2][2*n]+ans.val[1+n][2*n])%MOD))%MOD);
}
posted @ 2021-07-07 09:29  kid_magic  阅读(44)  评论(0编辑  收藏  举报