Q老师 对数列有一种非同一般的热爱,尤其是优美的斐波那契数列。
这一天,Q老师 为了增强大家对于斐波那契数列的理解,决定在斐波那契的基础上创建一个新的数列 f(x) 来考一考大家。数列 f(x) 定义如下:
当 x < 10 时,f(x) = x;
当 x ≥ 10 时,f(x) = a0 * f(x-1) + a1 * f(x-2) + a2 * f(x-3) + …… + a9 * f(x-10),ai 只能为 0 或 1。
Q老师 将给定 a0~a9,以及两个正整数 k m,询问 f(k) % m 的数值大小。
聪明的你能通过 Q老师 的考验吗?
Input
输出文件包含多组测试用例,每组测试用例格式如下:
第一行给定两个正整数 k m
。(k < 2e9, m < 1e5)
第二行给定十个整数,分别表示 a0~a9
。
Output
对于每一组测试用例输出一行,表示 f(k) % m
的数值大小。
Sample Input
10 9999
1 1 1 1 1 1 1 1 1 1
20 500
1 0 1 0 1 0 1 0 1 0
Sample Output
45
104
思路:
难点在于k取值范围太大,暴力从前往后推搞不定。为此我们可以采取一种很巧妙的方法——矩阵快速幂,这种方法经常可以用于降低线性递推的复杂度。
首先要构造矩阵的递推式,从而找到常数矩阵。矩阵递推式如下:
进而可以推导到如下:
最后答案就是F(n)的第一个数。
利用快速幂,可以很快计算出上式中矩阵的(n-9)
次方(注意矩阵乘法取模),从而解决问题。快速幂计算方法与整数的快速幂基本相同。
代码有详细注释。
代码:
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 12;
int a[maxn], k, M;
struct Matrix
{
int m[maxn][maxn];
void ini(int *a)
{//初始化矩阵A
memset(m, 0, sizeof(m));
for (int i = 1; i < maxn - 1; i++)
{
m[1][i] = a[i];
if (i < maxn - 2)
m[i + 1][i] = 1;
}
}
void ini1()
{//初始化矩阵F(9)
memset(m, 0, sizeof(m));
for (int i = 1; i < maxn - 1; i++)
{
m[i][1] = 10 - i;
}
}
Matrix() {} //默认构造
Matrix(const Matrix &t)
{ //复制构造
memcpy(m, t.m, sizeof(m));
}
Matrix operator*(const Matrix &t) const
{ //乘法
Matrix res;
for (int i = 1; i < maxn - 1; i++)
{
for (int j = 1; j < maxn - 1; j++)
{
res.m[i][j] = 0;
for (int k = 1; k < maxn - 1; k++)
{
res.m[i][j] += (m[i][k] * t.m[k][j]) % M;
}
res.m[i][j] %= M; //注意取模
}
}
return res;
}
};
Matrix quick_pow(Matrix a, Matrix b, int x)
{//快速幂
while (x)
{
if (x & 1)
{
b = a * b;
}
a = a * a;
x >>= 1;
}
return b;
}
Matrix A, f;
int main()
{
while (~scanf("%d %d", &k, &M))
{
for (int i = 1; i <= 10; i++)
scanf("%d", &a[i]);
A.ini(a);
f.ini1();
f = quick_pow(A, f, k-9);
printf("%d\n", f.m[1][1]);
}
return 0;
}