[SHOI2013]超級跳馬
阿新 • • 發佈:2021-07-07
題目分析
首先,可以先寫出一個 $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); }