HZAU 1202 GCD(斐波那契数列性质+矩阵快速幂)

原题

Problem D: GCD

Time Limit: 1 Sec  Memory Limit: 1280 MB

Description

Xiao Ming found the compute time of gcd(fibn,fibn+1) is the most when he learnt the gcd, and the result of it is always fib1, but he is not satisfied with the simple compute result.He wants to know what gcd(1+Sn,1+Sm) equals.
And gcd is greatest common divisor,

fib1=1,fib2=1,fibn=fibn−1+fibn−2(n≥3)

Sn=∑i=1nfibi

Input


 The first line is an positive integer  T . (1<=T<= 10^3) indicates the number of test cases. In the next T lines, there are three positive integer n, m, p (1<= n,m,p<=10^9) at each line.

Output

In each test case, output the compute result of gcd(1+Sn,1+Sm)%p at one line.
Sample Input

1
1 2 3

Sample Output


1

题意

    给出 n,m,p 三个整数,求斐波那契数列前 n 项加上1和与前 m 项和加上1的最大公约数并对它用p取模。

思路

    △斐波那契数列有这样两条性质:
      ①gcd(Fn,Fm)=Fgcd(n,m)
      ②1+Sn=Fn+2

    所以有gcd⁡(1+Sm,1+Sn)=Fgcd(m+2,n+2).用辗转相除法求最大公约数,用矩阵快速幂得到斐波那契数第 k 项。

涉及知识及算法

斐波那契数列:又称黄金分割数列、因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:1、1、2、3、5、8、13、21、34、……在数学上,斐波纳契数列以如下被以递归的方法定义:F(0)=0,F(1)=1, F(n)=F(n-1)+F(n-2)(n>=2,n∈N*).
通项公式 an=(1/√5)*{[(1+√5)/2]^n - [(1-√5)/2]^n}
奇数项求和 f(1)+f(3)+f(5)+…+f(2n-1)=f(2n)
偶数项求和 f(2)+f(4)+f(6)+…+f(2n) =f(2n+1)-1
平方求和 [f(0)]^2+[f(1)]^2+…+[f(n)]^2=f(n)·f(n+1)
两倍项关系 f(2n)/f(n)=f(n-1)+f(n+1)
——百度百科
矩阵快速幂:利用矩阵乘法的结合律,来减少重复计算的次数,高效地计算矩阵的高次方的。
例子:把n个矩阵进行两两分组,比如:A*A*A*A*A*A => (A*A)*(A*A)*(A*A)。这样变的好处是,你只需要计算一次A*A,然后将结果(A*A)连乘自己两次就能得到A^6,即(A*A)^3=A^6。算一下发现这次一共乘了3次,少于原来的5次。
对本题有矩阵

HZAU 1202 GCD(斐波那契数列性质+矩阵快速幂)_第1张图片

HZAU 1202 GCD(斐波那契数列性质+矩阵快速幂)_第2张图片

代码

#include 
#include 
#include 
#include 
using namespace std;
long long p;
struct matrix{
       long long a[2][2];
}origin,res;
void init(){      //初始化单位矩阵和要连乘的矩阵
    origin.a[0][0]=1;      
    origin.a[0][1]=1;
    origin.a[1][0]=1;
    origin.a[1][1]=0;
    res.a[0][0]=1;
    res.a[0][1]=0;
    res.a[1][0]=0;
    res.a[1][1]=1;
}
matrix multiply(matrix x,matrix y)   //矩阵乘法
{
       matrix temp;
       memset(temp.a,0,sizeof(temp.a));
       for(int i=0;i<2;i++)
               for(int j=0;j<2;j++)
                       for(int k=0;k<2;k++)
                               temp.a[i][j]=(temp.a[i][j]+x.a[i][k]*y.a[k][j])%p;
       return temp;
}
void calc(int n)      //矩阵快速幂
{
     while(n)
     {
             if(n&1) res=multiply(res,origin);   //利用二进制的特点来计算
             n>>=1;
             origin=multiply(origin,origin);
     }
}
int gcd(int big,int small){    
    int temp;
    while (small!=0){
        if(small>big) swap(big,small);
        temp=big%small;
        big=small;
        small=temp;
    }
    return big;
}
int main(){
    int T,n,m;
    scanf("%d",&T);
    while(T){
        scanf("%d %d %d",&n,&m,&p);
        init();
        int s=gcd(n+2,m+2);
        if(s>2) {calc(s-2); printf("%d\n",(res.a[0][0]+res.a[0][1])%p);} 
        else printf("1\n");
        T--;
    }
    return 0;
}
 
/**************************************************************
    Problem: 1202
    Language: C++
    Result: Accepted
    Time:1 ms
    Memory:1504 kb
****************************************************************/

图片引用自csdn博主柯安,向他表示感谢。

你可能感兴趣的:(HZAU,数论)