【洛谷 P1306】斐波那契公约数——杨子曰题目

【洛谷 P1306】斐波那契公约数——杨子曰题目

超链接:数学合集


题目描述
对于Fibonacci数列:1,1,2,3,5,8,13…大家应该很熟悉吧~~~但是现在有一个很“简单”问题:第n项和第m项的最大公约数是多少?

输入格式:
两个正整数n和m。(n,m<=10^9)

注意:数据很大

输出格式:
Fn和Fm的最大公约数。

由于看了大数字就头晕,所以只要输出最后的8位数字就可以了。

输入样例:

4 7

输出样例:

1

说明
用递归&递推会超时
用通项公式也会超时


这怕不是一道数学题……


看到题面,我们就可以猜测(别问我是怎么猜到的):
g c d ( f [ n ] , f [ m ] ) = f [ g c d ( n , m ) ] gcd(f[n],f[m])=f[gcd(n,m)] gcd(f[n],f[m])=f[gcd(n,m)]

于是我们就来证一证呗!


不过在证这个之前我们先要证明这样一个东东: g c d ( f [ n ] , f [ n + 1 ] ) = 1 gcd(f[n],f[n+1])=1 gcd(f[n],f[n+1])=1也就是斐波那契的相邻两项互质:

欧几里得算法(那是什么?戳)告诉我们:
g c d ( f [ n ] , f [ n + 1 ] ) = g c d ( f [ n ] , f [ n + 1 ] − f [ n ] ) gcd(f[n],f[n+1])=gcd(f[n],f[n+1]-f[n]) gcd(f[n],f[n+1])=gcd(f[n],f[n+1]f[n])
有没有发现 f [ n + 1 ] − f [ n ] = f [ n − 1 ] f[n+1]-f[n]=f[n-1] f[n+1]f[n]=f[n1],于是我们得到了:
g c d ( f [ n ] , f [ n + 1 ] ) = g c d ( f [ n ] , f [ n − 1 ] ) gcd(f[n],f[n+1])=gcd(f[n],f[n-1]) gcd(f[n],f[n+1])=gcd(f[n],f[n1])
嗯?这不就说明斐波那契数列所有相邻两项的gcd都相等吗?

显然:gcd(f[1],f[2])=1,这样一来所有的相邻两项gcd就都是1了

得证!


咱们正式开始证明: g c d ( f [ n ] , f [ m ] ) = f [ g c d ( n , m ) ] gcd(f[n],f[m])=f[gcd(n,m)] gcd(f[n],f[m])=f[gcd(n,m)]

首先我们假设 n < m , f [ n ] = a , f [ n + 1 ] = b nn<m,f[n]=a,f[n+1]=b
这样一来我们往后推一下发现:
f [ n + 1 ] = a + b , f [ n + 2 ] = a + 2 b , f [ n + 3 ] = 2 a + 3 b ⋯ ⋯ f [ m ] = f [ m − n − 1 ] a + f [ m − n ] b f[n+1]=a+b,f[n+2]=a+2b,f[n+3]=2a+3b \cdots \cdots f[m]=f[m−n−1]a+f[m−n]b f[n+1]=a+b,f[n+2]=a+2b,f[n+3]=2a+3bf[m]=f[mn1]a+f[mn]b
(↑不明白的童鞋自己多模拟几个↑)

然后我们把a和b代掉:
f [ m ] = f [ m − n − 1 ] ∗ f [ n ] + f [ m − n ] ∗ f [ n + 1 ] f[m]=f[m−n−1]∗f[n]+f[m−n]∗f[n+1] f[m]=f[mn1]f[n]+f[mn]f[n+1]
这样一来:
g c d ( f [ n ] , f [ m ] ) = g c d ( f [ n ] , f [ m − n − 1 ] ∗ f [ n ] + f [ m − n ] ∗ f [ n + 1 ] ) gcd(f[n],f[m])=gcd(f[n],f[m−n−1]∗f[n]+f[m−n]∗f[n+1]) gcd(f[n],f[m])=gcd(f[n],f[mn1]f[n]+f[mn]f[n+1])

f [ m − n − 1 ] ∗ f [ n ] f[m−n−1]∗f[n] f[mn1]f[n] f [ n ] f[n] f[n]的倍数,说明他对这个gcd毫无影响,也就是说:
g c d ( f [ n ] , f [ m ] ) = g c d ( f [ n ] , f [ m − n ] ∗ f [ n + 1 ] ) gcd(f[n],f[m])=gcd(f[n],f[m−n]∗f[n+1]) gcd(f[n],f[m])=gcd(f[n],f[mn]f[n+1])
上面已经证过了: g c d ( f [ n ] , f [ n + 1 ] ) = 1 gcd(f[n],f[n+1])=1 gcd(f[n],f[n+1])=1,说明 f [ n + 1 ] f[n+1] f[n+1]对这个gcd值毫无任何贡献,我们得到了:
g c d ( f [ n ] , f [ m ] ) = g c d ( f [ n ] , f [ m − n ] ) gcd(f[n],f[m])=gcd(f[n],f[m−n]) gcd(f[n],f[m])=gcd(f[n],f[mn])
这个式子就很神奇了,有没有发现它有点像欧几里得算法(戳我),我们可以得到的是:
g c d ( f [ n ] , f [ m ] ) = g c d ( f [ n ] , f [ m   m o d   n ] ) gcd(f[n],f[m])=gcd(f[n],f[m\ mod\ n]) gcd(f[n],f[m])=gcd(f[n],f[m mod n])
简单解释一下这一步是怎么推过来的,对于上面的上面的那个式子,如果我们把 f [ n − m ] f[n-m] f[nm]这看成等式左边一项新的 f [ m ] f[m] f[m]就可以继续把下标减n,一直减一直减,直到不能减了为止,就是 m   m o d   n m\ mod \ n m mod n(和欧几里得算法的想法是一样滴)

根据这个式子: g c d ( f [ n ] , f [ m ] ) = g c d ( f [ n ] , f [ m   m o d   n ] ) gcd(f[n],f[m])=gcd(f[n],f[m\ mod\ n]) gcd(f[n],f[m])=gcd(f[n],f[m mod n])我们就可以像欧几里得算法一样不停的的递归下去了

直到m变成了0,答案就是f[n],有没有发现这时的n就是最开始的gcd(m,n)

得证


题目已经告诉你了不要用递归,递推,通项公式之类的东西

So,我们不得不 使用矩阵快速幂(你也可以使用一个更加神奇的方法,在O(log n)的时间里求出斐波那契第n项,戳我,这篇文章里就不多讲了)

最终想法:用矩阵快速幂求出f[gcd(n,m)] mod 100000000,即为答案
复杂度:O(log n)

OK,完事


c++代码:

#include 
using namespace std;
 
int p=100000000;
 
struct Matrix{
    int a[3][3];
    void init(){
        memset(a,0,sizeof(a));
    }
    void one(){
        memset(a,0,sizeof(a));
        for (int i=1;i<=2;i++){
            a[i][i]=1;
        }
    }
}f; 
 
Matrix operator * (Matrix x,Matrix y){
    Matrix c;
    c.init();
    for (int i=1;i<=2;i++){
        for (int j=1;j<=2;j++){
            for (int k=1;k<=2;k++){
                c.a[i][j]=(c.a[i][j]+1LL*x.a[i][k]*y.a[k][j])%p;
            }
        }
    } 
    return c;
}
 
Matrix operator ^ (Matrix x,int k){
    Matrix ans;
    ans.one();
    while(k){
        if (k&1) ans=ans*x;
        x=x*x;
        k>>=1;
    }
    return ans;
}

int gcd(int a,int b){
	return b==0?a:gcd(b,a%b);
}

int fib(int n){
	    if (n==0) return 0;
        if (n==1) return 1;
        f=f^(n-2);
        return (f.a[1][2]+f.a[2][2])%p;
}
 
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    f.a[1][1]=0;
    f.a[1][2]=1;
    f.a[2][1]=1;
    f.a[2][2]=1;
    cout<<fib(gcd(n,m));
    return 0;
}

参考:https://www.luogu.org/blog/five20/solution-p1306
于HG机房

你可能感兴趣的:(恶心的题目)