1. 程式人生 > 其它 >[SHOI2013]超級跳馬

[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);
}