UVA_10518
这个题目想到f(n)=f(n-1)+f(n-2)+1还是比较容易的,但如果能想到是f(n)=2*F(n)-1就不太容易了,在看了UVA的论坛之后我才知道原来可以表示成这个样子,其中F(n)为斐波那契数,有了这个式子是第一步,后面的计算过程倒还不算麻烦。
后来在群里讨论的时候,突然发现S(n)=F(n)+F(n+1)-1,S(n)为斐波那契的前n项和,这时又想到我之前推到的一个结论f(n)=S(n)-F(n-1),发现这个和f(n)=2*F(n)-1是可以转化的,限于当时还不知道S(n)的表达式,所以错过了推出结论的机会。
先说说我推出f(n)=S(n)-F(n)的过程吧,我们在递推计算递归次数的时候,每次的+1都另起一行来写,这样我们会得到下面的这个表。
f(0) f(1) f(2) f(3) f(4) f(5) f(6)
1
1 1
1 1 2
1 1 2 3
1 1 2 3 5
1 1 2 3 5 8 13
没有数字的地方就可以看做是0,f(i)下面对应的整数和就是f(i)的值,相当于计算f(n)=f(n-1)+f(n-2)+1的时候,每行都进行f(n)=f(n-1)+f(n)的运算,最后再把1另起一行写在上面。
这样我们就能很明显的看出来,每行都是一个斐波那契数列,不难得到f(n)=S(n)-F(n-1),这样再把S(n)= F(n)+F(n+1)-1代入,就可以得到f(n)=2*F(n)-1。
不管用什么办法,反正我们现在是得到f(n)的表达式了。下面回到题目,问题就转化成了f(n)%b的值是多少,由于n很大所以我们在计算F(n)的时候不能直接递推计算,于是可以把F(n)写成矩阵的形式,然后用快速幂取模求得F(n)%b的值,进而就会有f(n)%b的值。
#include<stdio.h>
#include<string.h>
#include<math.h>
long long int N, B;
long long int a[100][5], b[5];
void pow_mod(long long int n, int e)
{
if(n == 1)
{
a[e][0] = a[e][1] = a[e][2] = 1;
a[e][3] = 0;
return ;
}
pow_mod(n / 2, e + 1);
a[e][0] = (a[e + 1][0] * a[e + 1][0] + a[e + 1][1] * a[e + 1][2]) % B;
a[e][1] = (a[e + 1][0] * a[e + 1][1] + a[e + 1][1] * a[e + 1][3]) % B;
a[e][2] = (a[e + 1][2] * a[e + 1][0] + a[e + 1][3] * a[e + 1][2]) % B;
a[e][3] = (a[e + 1][2] * a[e + 1][1] + a[e + 1][3] * a[e + 1][3]) % B;
if(n % 2)
{
b[0] = (a[e][0] * a[0][0] + a[e][1] * a[0][2]) % B;
b[1] = (a[e][0] * a[0][1] + a[e][1] * a[0][3]) % B;
b[2] = (a[e][2] * a[0][0] + a[e][3] * a[0][2]) % B;
b[3] = (a[e][2] * a[0][1] + a[e][3] * a[0][3]) % B;
a[e][0] = b[0], a[e][1] = b[1], a[e][2] = b[2], a[e][3] = b[3];
}
}
void solve()
{
a[0][0] = a[0][1] = a[0][2] = 1;
a[0][3] = 0;
if(N == 0)
{
printf("1\n");
return ;
}
pow_mod(N, 1);
printf("%lld\n", (2 * a[1][0] + B - 1) % B);
}
int main()
{
int t = 0;
for(;;)
{
scanf("%lld%lld", &N, &B);
if(!N && !B)
break;
printf("Case %d: %lld %lld ", ++ t, N, B);
solve();
}
return 0;
}