一、题意
1.简述
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老师 的考验吗?
2.输入格式
输出文件包含多组测试用例,每组测试用例格式如下:
第一行给定两个正整数 k m。(k < 2e9, m < 1e5)
第二行给定十个整数,分别表示 a0~a9。
3.输出格式
对于每一组测试用例输出一行,表示 f(k) % m 的数值大小。
4.样例
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
Output
45
104
二、算法
主要思路
什么是矩阵快速幂?
考虑如下情景:给定矩阵 AAA,请快速计算出AnA^nAn(nnn 个矩阵 AAA 相乘)的结果,输出的每个数都 %p\%p%p。
矩阵快速幂类似于一般的快速幂。只不过我们需要为矩阵类重载乘号,然后直接调用前面学过的整数的快速幂的函数即可。
这个题为什么能用矩阵快速幂?
因为该题有矩阵的连乘。
我们再根据线性递推式(比如该题中的f(x)=a0∗f(x−1)+a1∗f(x−2)+a2∗f(x−3)+……+a9∗f(x−10)f(x) = a0 * f(x-1) + a1 * f(x-2) + a2 * f(x-3) + …… + a9 * f(x-10)f(x)=a0∗f(x−1)+a1∗f(x−2)+a2∗f(x−3)+……+a9∗f(x−10))求解某一个值(比如f(k)f(k)f(k))时,如果直接按照线性递推式线性求解,遍历每一个点,那么必然会超时,因为该题的k<2e9k \lt 2e9k<2e9。
但是我们可以将线性递推式的一步步推导转化为有规律的矩阵的连乘。对于该题,当k≥10k\ge10k≥10时,
[f(x)f(x−1)...f(x−9)]=[a0a1...a910...001...0............00...1]⋅[f(x−1)f(x−2)...f(x−10)]
\begin{bmatrix}
f(x) \\
f(x-1) \\
... \\
f(x-9)\\
\end{bmatrix} =
\begin{bmatrix}
a_0& a_1 & ... & a_9 \\
1 & 0 & ... & 0 \\
0 & 1 & ... & 0\\
... &...&...&...\\
0 & 0 &...& 1 \\
\end{bmatrix} \cdot
\begin{bmatrix}
f(x-1) \\
f(x-2) \\
... \\
f(x-10)\\
\end{bmatrix}
⎣⎢⎢⎡f(x)f(x−1)...f(x−9)⎦⎥⎥⎤=⎣⎢⎢⎢⎢⎡a010...0a101...0...............a900...1⎦⎥⎥⎥⎥⎤⋅⎣⎢⎢⎡f(x−1)f(x−2)...f(x−10)⎦⎥⎥⎤一直推导可以推导出:[f(x)f(x−1)...f(x−9)]=[a0a1...a910...001...0............00...1]x−9⋅[f(9)f(8)...f(0)]
\begin{bmatrix}
f(x) \\
f(x-1) \\
... \\
f(x-9)\\
\end{bmatrix} =
{\begin{bmatrix}
a_0& a_1 & ... & a_9 \\
1 & 0 & ... & 0 \\
0 & 1 & ... & 0\\
... &...&...&...\\
0 & 0 &...& 1 \\
\end{bmatrix}}^{x-9} \cdot
\begin{bmatrix}
f(9) \\
f(8) \\
... \\
f(0)\\
\end{bmatrix}
⎣⎢⎢⎡f(x)f(x−1)...f(x−9)⎦⎥⎥⎤=⎣⎢⎢⎢⎢⎡a010...0a101...0...............a900...1⎦⎥⎥⎥⎥⎤x−9⋅⎣⎢⎢⎡f(9)f(8)...f(0)⎦⎥⎥⎤
然后就可以利用矩阵快速幂了。
快速幂能将时间复杂度从O(n)O(n)O(n)降到O(logn)O(logn)O(logn)。
三、代码
#include<iostream>
#include<cstring>
#include<string>
#include<cmath>
using namespace std;
int a[10];
const int N = 10;
int k,m;
struct Matrix{
int x[N+1][N+1];
Matrix operator*(const Matrix& r)const{
Matrix rtn;
for(int i=1;i<=N;i++){
for(int j=1;j<=N;j++){
rtn.x[i][j] = 0;
for(int k=1;k<=N;k++){
rtn.x[i][j] += (x[i][k]*r.x[k][j])%m;
rtn.x[i][j] %= m;//这行必须加,因为rtn.x[i][j]有可能在累加的过程中超过m
}
}
}
return rtn;
}
};
Matrix quick_pow(Matrix a,int x){
Matrix rtn;
for(int i=1;i<=N;i++)
for(int j=1;j<=N;j++)
rtn.x[i][j] = i==j?1:0;
while(x){
if(x&1) rtn = rtn*a;
a = a*a;
x >>= 1;
}
return rtn;
}
int main(){
while(scanf("%d%d",&k,&m)!=EOF){
for(int i=0;i<10;i++) scanf("%d",&a[i]);
Matrix ans;
for(int i=1;i<=N;i++) ans.x[1][i] = a[i-1];
for(int i=2;i<=N;i++){
for(int j=1;j<=N;j++){
ans.x[i][j] = i==j+1?1:0;
}
}
if(k<10) printf("%d\n",k);
else{
int num = 0;
ans = quick_pow(ans,k-9);
for(int i=1;i<=N;i++){
num += (ans.x[1][i]*(10-i))%m;
num %= m;
}
printf("%d\n",num);
}
}
return 0;
}
/*
*/