[矩阵乘法][DP]JZOJ 3294 超级跳马
分析
这题,你敢信我比赛的时候居然没想到前缀和优化DP……
f[i][j]表示第j行第i列的方案数,则有
$f[i][j]=\sum_{k=1}^{i/2}f[i-2*k+1][j\pm 1]$
设s1[i][j]表示距离当前列有奇数列(奇数猎?)的前缀和,s2差不多偶数猎
$s1[i][j]=s1[i-1][j\pm 1]+s2[i-1][j]$
$s2[i][j]=s1[i-1][j]$
可以把s2换成s1
$s1[i][j]=s1[i-1][j\pm 1]+s1[i-2][j]$
直接推会爆,所以可以矩阵乘法优化
设一个$1\times 2n$的矩阵A,前n个为s1[i],后n个为s1[i-1]
则要有矩阵T与A相乘得出B[s1[i+1],s1[i]]
然后根据上面的柿子,T为
?,单位矩阵
单位矩阵,空矩阵
?处可以较显然地看出为
1,1,0,0,0
1,1,1,0,0
0,1,1,1,0
0,0,1,1,1
0,0,0,1,1
(n=5)
然后快速幂即可
最后记住最后一个位置只能从两行转移,所以答案为s1[m][n]-s1[m-2][n]
#include <iostream> #include <cstdio> #include <memory.h> using namespace std; const int N=110; const int P=3e4+11; int n,m; struct Rect { int r[N][N]; Rect operator * (Rect &b) { Rect c;memset(c.r,0,sizeof c.r); for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) for (int k=1;k<=n;k++) (c.r[i][j]+=r[i][k]*b.r[k][j]%P)%=P; return c; } }T,A,B; Rect Pow(Rect x,int y) { Rect ans;memset(ans.r,0,sizeof ans.r); for (int i=1;i<=n;i++) ans.r[i][i]=1; while (y) { if (y&1) ans=ans*x; x=x*x; y>>=1; } return ans; } int main() { scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) T.r[i][i]=T.r[i+n][i]=T.r[i][i+n]=1; for (int i=2;i<=n;i++) T.r[i-1][i]=T.r[i][i-1]=1; n<<=1; A=Pow(T,m-2);B=A*T; printf("%d",(B.r[1][n>>1]-A.r[1][n]+P)%P); }
在日渐沉没的世界里,我发现了你。