试题 算法训练 小X的暑假作业
资源限制
时间限制:1.0s 内存限制:128.0MB
问题描述
小X的作业是老师给他布置的,题目很简单,求出第N个斐波那契数 mod P的值。小X怎么想也想也想不出来,只好求助于你了。
输入格式
第一行为数据组数T。
第二行开始,以下T行,每行为老师给小X布置的作业中的N和P。
输出格式
包含T行,每行是一个作业的答案。
样例输入
3
7 3
1000000 89
987654321 30000
样例输出
0
55
19111
数据规模与约定
对于50%的数据,T<=100,N<=10000。
对于100%的数据,T<=10000,N<=1000000000,P<=30000。
斐波那契数的第0个是1。
试题解析
斐波那契数列递推式为:f(n + 2) = f(n + 1) + f(n)(n为非负整数)
使用滚动数组去求会超时,下面我用的时矩阵的快速幂的方法。
mat代表矩阵,E为单位矩阵,B为转移矩阵。
根据递推式建立矩阵An=[f(n + 1), f(n)] (易得A0=[f(1), f(0)]=[1, 1]),下面还需要转移矩阵B使得
AnB=[f(n + 2), f(n + 1)]=An+1,那么An=A0Bn,根据递推式可得转移矩阵B如下
即问题变成求矩阵B的幂的问题了对矩阵B快速幂即可,最后与A0对应相乘即可
代码
#include <stdio.h>
typedef long long ll;
typedef struct ma{
ll m[2][2];
}mat;
ll mod;
mat E = {0}, B = {0};
mat sq(mat a, mat b) {//求矩阵a*矩阵b
mat c;
int i, j, k;
for(i = 0;i < 2; ++i) {
for(j = 0;j < 2; ++j) {
c.m[i][j] = 0;
for(k = 0;k < 2; ++k)
c.m[i][j] = (c.m[i][j] + a.m[i][k] * b.m[k][j]) % mod;
}
}
return c;
}
mat qpow(mat a, ll n) {// 求矩阵a的n次方
mat ans = E;
for(;n; n >>= 1, a = sq(a, a)) {
if(n&1) ans = sq(ans, a);
}
return ans;
}
int main() {
ll t, i, n, num[10000];
scanf("%lld", &t);
E.m[0][0] = E.m[1][1] = 1;
for(i = 0;i < t; ++i) {
B.m[0][0] = B.m[1][0] = B.m[0][1] = 1;
B.m[1][1] = 0;
scanf("%lld%lld", &n, &mod);
mat ans = qpow(B, n - 1);
num[i] = (ans.m[0][0] + ans.m[1][0]) % mod;
}
for(i = 0;i < t; ++i)
printf("%lld\n", num[i]);
return 0;
}