Yet Another Number Sequence 题解
见数据范围知算法,很明显,矩阵快速幂!
但是矩阵快速幂只能解决 线性递推 的问题,所以我们需要转化!
怎么转呢?自已观察题目,可以发现最棘手的问题在这里:
这里因为
既然是 线性递推 ,我们就肯定要拆解,考虑使用 二项式定理 展开!
接下来使用二项式定理展开
可以发现,转移需要所有的
那不就简单了,带上
可以看到,我们只需要同时保留
那么我们就可以开始了!矩阵就定义成这样吧:
那么递推方程就应该长这样,照着上面的式子写,分分块,挺有规律的:
之中还有很多有趣的东西:
- 第一区的
1 ,表示S 的递增。 - 第二、三区的 全零矩阵 ,表示
(i-1)^nF_{i-1} 和i^nF_i 的转移与S 无关。 - 第八区的 单位矩阵 ,表示一次转移之后
(i-1)^nF_{i-1} 直接被对应的i^nF_i 所覆盖。类似的,第五区的 全零矩阵 表示直接抛弃。 - 第九区就是 杨辉三角 !
-
还有很多,你愿意的话可以自己发掘一下~
最后附上代码,带空格属于是老年人码风力:
#include <iostream>
#include <cstring>
using namespace std;
#define int unsigned long long
#define mod 1000000007
#define MATRIXSIZE 125
struct matrix {
int m[MATRIXSIZE][MATRIXSIZE], w, h;
matrix(int width, int height) : w(width), h(height) {
memset(m, 0, sizeof(m));
}
int * operator [] (int p) {
return m[p];
}
matrix operator * (matrix x) const {
matrix res(x.w, h);
for (int a = 1; a <= h; a ++)
for (int b = 1; b <= x.w; b ++)
for (int c = 1; c <= w; c ++)
res[a][b] = (res[a][b] +
m[a][c] * x[c][b] % mod) % mod;
return res;
}
matrix operator ^ (int k) const {
matrix res(w, w), times(*this);
for (int i = 1; i <= w; i ++)
res[i][i] = 1;
while (k) {
if (k & 1)
res = res * times;
times = times * times;
k >>= 1;
}
return res;
}
void output() const {
cout << "width: " << w << endl;
cout << "height: " << h << endl;
for (int i = 1; i <= 2; i ++) {
cout << "[ ";
for (int j = 1; j <= w; j ++)
cout << m[i][j] << ' ';
cout << ']' << endl;
}
}
};
int n, k;
int solvo() {
int siz = 1 + ((++ k) << 1);
matrix res(siz, 1), times(siz, siz);
res[1][1] = res[1][2] = 1;
for (int i = 2 + k; i <= siz; i ++)
res[1][i] = 1;
// 第一区
times[1][1] = 1;
// 第八区
for (int i = 2; i <= 1 + k; i ++)
times[i + k][i] = 1;
// 第九区
times[2 + k][2 + k] = 1;
for (int i = 3 + k; i <= siz; i ++)
for (int j = 2 + k; j <= i; j ++)
times[j][i] = (times[j][i - 1] + times[j - 1][i - 1]) % mod;
// 第六区
for (int i = 2; i <= 1 + k; i ++)
for (int j = i + k; j <= siz; j ++)
times[i][j] = (times[i + k][j] << (j - i - k)) % mod;
// 第四及第七区
for (int i = 2; i <= siz; i ++)
times[i][1] = times[i][siz];
times = times ^ (n - 1);
res = res * times;
return res[1][1];
}
main() {
cin >> n >> k;
cout << solvo();
return 0;
}
拜拜~