P5004 专心OI - 跳房子
Solution:
考虑线性dp。\(f[i]\)表示前\(i\)个格子(仅限于跳到前\(i\)个格子)有多少种跳法。则\(f[i] = f[1] + f[2] + ... + f[i - M - 1]\)。(不会打xigema,凑合着看吧)
然而\(N\)很大,无法用\(\operatorname{O}(N)\)的时间来做。发现\(M\)很小,立刻想到矩阵乘法优化线性dp。
维护一个\(M+1\)行\(1\)列的矩阵\(A\),\(A_{11}=f[i],A_{21}=f[i-1],...A_{(M+1)1}=f[i-M]\)。
初始矩阵为\(M+1\)行\(1\)列的矩阵\(A\),为\(f\)数组的前\(M+1\)个数值。事实上\(f\)数组的前\(M+1\)个数值全部都是\(1\)。所以初始化的矩阵的元素都是\(1\)(即从最左边直接跳到该位置)。
如何转移下图的矩阵?注意到左右侧相同元素均可匹配,那么现在就求\(f[i]\)。
\(f[i] = f[i-1]+f[i-M-1]\)。由第一行的公式可知\(f[i-1]\)代表的就是\(f[1]+f[2]+ ... +f[i-M-2]\)。
答案:将最后的f数组的元素都加起来。因为现在在这些格子上都可以一步跳到无限大。
时间复杂度\(\operatorname{O}(M^3logN)\)。

Code:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long LL;
const int MAXM = 17 + 10;
const int SIZ = 17;
const LL MOD = 1000000007;
LL N, M;
struct Matrix {
LL l[MAXM][MAXM];
Matrix () {memset(l, 0, sizeof(l));}
Matrix operator = (Matrix h) {
for(int i = 1; i <= SIZ; i++)
for(int j = 1; j <= SIZ; j++)
l[i][j] = h.l[i][j] % MOD;
return *this;
}
Matrix operator * (Matrix h) {
Matrix c;
for(int k = 1; k <= SIZ; k++)
for(int i = 1; i <= SIZ; i++)
for(int j = 1; j <= SIZ; j++)
c.l[i][j] += (l[i][k] * h.l[k][j]) % MOD;
return c;
}
}T, bas, st;
void init() {
bas.l[1][1] = bas.l[1][M + 1] = 1;
// bas为转移的矩阵,st为初始矩阵,T为单位矩阵。
for(int i = 2; i <= M + 1; i++)
bas.l[i][i - 1] = 1;
for(int i = 1; i <= M + 1; i++)
st.l[i][1] = 1;
for(int i = 1; i <= M + 1; i++)
T.l[i][i] = 1;
}
Matrix Qpow(Matrix a, LL b) { // 矩阵快速幂优化
Matrix res = T;
while(b > 0) {
if(b & 1) res = res * a;
a = a * a;
b >>= 1;
}
return res;
}
void Wor() {
Matrix h = Qpow(bas, N - M);
st = h * st; // 注意矩阵乘法满足结合律但不满足交换律!st=st*h和st=h*st是不一样的!
LL ans = 0;
for(int i = 1; i <= SIZ; i++)
// for(int j = 1; j <= SIZ; j++)
ans = (ans + st.l[i][1]) % MOD;
printf("%lld", ans);
}
int main() {
#ifdef test
freopen("test.txt", "r", stdin);
#endif
cin >> N >> M;
init();
Wor();
return 0;
}

浙公网安备 33010602011771号