洛谷 P1306 斐波那契公约数(数学)

题意

求斐波那契数列第 n n n项和第 m m m项的最大公约数( n , m ≤ 2 × 1 0 9 n,m \leq 2 \times 10^9 n,m2×109

题解

数据范围明确表明这题不是找规律就是推奇怪的结论。

于是先开始找规律。假设 f ( x ) f(x) f(x)表示有x因子的数在斐波那契数列中出现的循环节长度。之所以这么说是因为打表发现拥有某个因子的数非常有规律,比如 2 2 2(被 2 2 2整除记为 Y Y Y,反之 N N N): N N Y    N N Y    N N Y    N N Y    N N Y ⋯ NNY \; NNY \; NNY \; NNY \; NNY \cdots NNYNNYNNYNNYNNY。然后 f ( x ) f(x) f(x)还是积性函数。于是问题变成了求 f ( x ) f(x) f(x) n n n m m m的公约数的最大的 x x x

但是这有什么用呢?

然后我开始试图推结论。假设 n ≤ m n \leq m nm,于是 f b ( m ) fb(m) fb(m)可以表示为 f b ( m − n ) × f b ( n − 1 ) + f b ( m − n + 1 ) × f b ( n ) fb(m-n) \times fb(n-1) + fb(m-n+1) \times fb(n) fb(mn)×fb(n1)+fb(mn+1)×fb(n),只要把 f b ( m ) fb(m) fb(m) f b ( m − 1 ) , f b ( m − 2 ) … fb(m-1),fb(m-2) \dots fb(m1),fb(m2)表示,最后就能得到这个式子。

于是

G c d ( f b ( n ) , f b ( m ) ) = G c d ( f b ( n ) , f b ( m − n ) × f b ( n − 1 ) + f b ( m − n + 1 ) × f b ( n ) ) Gcd(fb(n),fb(m))=Gcd(fb(n),fb(m-n) \times fb(n-1) + fb(m-n+1) \times fb(n)) Gcd(fb(n),fb(m))=Gcd(fb(n),fb(mn)×fb(n1)+fb(mn+1)×fb(n))

= G c d ( f b ( n ) , f b ( m − n ) × f b ( n − 1 ) ) =Gcd(fb(n),fb(m-n) \times fb(n-1)) =Gcd(fb(n),fb(mn)×fb(n1))

又于是,我觉得这个式子没有什么利用价值,于是放弃了。
燃鹅??? f b ( n ) fb(n) fb(n) f b ( n − 1 ) fb(n-1) fb(n1)互质,于是这个公式可以再少一项,即:

G c d ( f b ( n ) , f b ( m ) ) = G c d ( f b ( n ) , f b ( m − n ) ) Gcd(fb(n),fb(m))=Gcd(fb(n),fb(m-n)) Gcd(fb(n),fb(m))=Gcd(fb(n),fb(mn))

最后

G c d ( f b ( n ) , f b ( m ) ) = G c d ( f b ( G c d ( n , m ) ) , 0 ) = f b ( G c d ( n , m ) ) Gcd(fb(n),fb(m))=Gcd(fb(Gcd(n,m)),0)=fb(Gcd(n,m)) Gcd(fb(n),fb(m))=Gcd(fb(Gcd(n,m)),0)=fb(Gcd(n,m))

于是利用结论打个矩阵快速幂这题就结束了。不过想手推结论还是不容易。

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long 
using namespace std;
const int mod = 1e8;
int a[2][2], b[2][2];

int gcd(int x, int y)
{
    while (y){
        int rem = x%y;
        x = y;
        y = rem;
    }
    return x;
}

void Mul(int a1[2][2], int b1[2][2]) // 史上最丑快速幂
{
    int c[2][2];
    memset(c, 0, sizeof(c));
    for (int i = 0; i < 2; i++)
        for (int j = 0; j < 2; j++)
            for (int k = 0; k < 2; k++)
                (c[i][j] += (ll)a1[i][k]*b1[k][j]%mod) %= mod;
    for (int i = 0; i < 2; i++)
        for (int j = 0; j < 2; j++)
            a1[i][j] = c[i][j];
}

void Ksm(int k)
{
    while (k){
        if (k&1) Mul(a, b);
        Mul(b, b);
        k >>= 1;
    }
}

int main()
{
    int n, m, d;
    cin >> n >> m;
    d = gcd(n, m);
    a[0][0] = a[0][1] = 1;
    b[0][0] = b[0][1] = b[1][0] = 1;
    Ksm(d-1);
    cout << a[0][1]%mod;
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值