bzoj4417 [Shoi2013]超级跳马
传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4417
【题解】
令f[i,j]表示到第2i-1列第j行的方案数,g[i,j]表示到第2i行第j列的方案数。
那么有
f[i,j]=Σg[1..i-1,j]+Σg[1..i-1,j-1]+Σg[1..i-1,j+1]
g[i,j]=Σf[1..i,j]+Σf[1..i,j-1]+Σf[1..i,j+1]
将f[i,j]和g[i,j]重新表示为前缀和形式
f[i,j]=f[i-1,j]+g[i-1,j]+g[i-1,j-1]+g[i-1,j+1]
g[i,j]=g[i-1,j]+f[i,j]+f[i,j-1]+f[i,j+1]
容易想到矩阵乘法
按照这种形式排列
f[i,1], f[i,2], ..., f[i,n], g[i,1], g[i,2], ..., g[i,n]
那么g转移要用到f怎么办?
我们想,让这个矩阵连续乘2个转移矩阵
第一个转移完f和g的前缀和部分。到乘第二个矩阵的时候,f已经转移完了,就能用f的信息了。
真正矩阵乘法把两个转移矩阵乘在一起做就行了。
要拓宽思路不要想着一个转移矩阵搞定问题。
注意判断m是奇偶的问题,以及n=1和不用做矩阵乘法等特判
# include <stdio.h> # include <assert.h> # include <string.h> # include <iostream> # include <algorithm> // # include <bits/stdc++.h> using namespace std; typedef long long ll; typedef long double ld; typedef unsigned long long ull; const int M = 150 + 10; const int mod = 30011; # define RG register # define ST static struct matrix { int a[M][M], n, m; inline void set(int _n, int _m) { n = _n, m = _m; memset(a, 0, sizeof a); } friend matrix operator *(matrix a, matrix b) { matrix c; c.set(a.n, b.m); assert(a.m == b.n); for (int i=1; i<=c.n; ++i) for (int j=1; j<=c.m; ++j) for (int k=1; k<=a.m; ++k) c.a[i][j] = (c.a[i][j] + a.a[i][k] * b.a[k][j]) % mod; return c; } friend matrix operator ^(matrix a, int b) { matrix c; c.set(a.n, a.m); assert(a.n == a.m); for (int i=1; i<=c.n; ++i) c.a[i][i] = 1; while(b) { if(b&1) c = c*a; a = a*a; b >>= 1; } return c; } inline void prt() { for (int i=1; i<=n; ++i, cout << endl) for (int j=1; j<=m; ++j) cout << a[i][j] << " "; } }A, T1, T2, T, B; int n, m; int main() { cin >> n >> m; T1.set(n+n, n+n), T2.set(n+n, n+n); for (int i=1; i<=n; ++i) { T1.a[i][i] = 1; T2.a[i][i] = 1; T1.a[i+n][i] = 1; T2.a[i][i+n] = 1; if(i != 1) { T1.a[i-1+n][i] = 1; T2.a[i-1][i+n] = 1; } if(i != n) { T1.a[i+1+n][i] = 1; T2.a[i+1][i+n] = 1; } T1.a[i+n][i+n] = 1; T2.a[i+n][i+n] = 1; } T = T1*T2; A.set(1, n+n); A.a[1][1] = 1; A.a[1][1+n] = 1; if(n != 1) A.a[1][1+n+1] =1; int times = (m-1)/2; if(times == 0) { if(m == 1) cout << A.a[1][n]; else cout << A.a[1][n+n]; return 0; } --times; A = A * (T^times); B = A * T; if(m&1) cout << ((B.a[1][n] - A.a[1][n]) % mod + mod) % mod; else cout << ((B.a[1][n+n] - A.a[1][n+n]) % mod + mod) % mod; return 0; }