• P3986 斐波那契数列(扩欧求逆元)


    思路

    当我刚开始看到这个题的时候,还没有什么思路,但是当我列出了几个关于 (a,b,k) 的式子:

    [a+b=k\a+2b=k\2a+3b=k\3a+5b=k\dots ]

    可能还不太明显,仔细观察 (a,b) 的系数,可以发现,这不就是斐波那契数列吗!!!

    那么我们设 (f(x)) 为斐波那契数列的第 (x) 项,那么可以转化为:

    [f(0)*a+f(1)*b=k\f(1)*a+f(2)*b=k \f(2)*a+f(3)*b=k\dots\f(x-1)*a+f(x)*b=k ]

    那么我们就可以枚举 (x)

    然后,移项,可得:

    [b=dfrac{k-f(x-1)*a}{f(x)} ]

    由于 (b) 为整数,所以相当于求多少 (a) 可以使得 (f(x)|k-f(x-1)*a) 成立,即:

    [k-f(x-1)*aequiv 0~(mod~f(x))\f(x-1)*aequiv k~(mod~f(x))\aequiv k*inv(f(x-1))~ (mod~f(x)) ]

    由于 (f(x))(f(x-1)) 互质(这里就不证明了),可以直接扩欧干上去求逆元,然后差不多了

    小trick:因为 (k-f(x-1)*a>0)(a<dfrac{k}{f(x-1)}),所以可以枚举的更少了)

    代码

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #define ll long long
    
    using namespace std;
    const int N=45;
    const int n=40+2;
    const int mod=1000000007;
    ll f[N],K,ans;
    
    ll exgcd(ll A,ll B,ll &x,ll &y)
    {
        if(B==0)
        {
            x=1,y=0;
            return A;
        }
        ll temp=exgcd(B,A%B,x,y),z=x;
        x=y,y=z-(A/B)*y;
        return temp;
    }
    
    ll inv(ll A,ll POI)
    {
        ll t,tt;
        exgcd(A,POI,t,tt);
        return (t%POI+POI)%POI;
    }
    
    int main()
    {
        scanf("%lld",&K); 
        f[1]=f[2]=1;
        for(int i=3;i<=n;i++)
            f[i]=f[i-1]+f[i-2];
        for(int i=2;i<=n;i++)
        {
            ll a=(K*inv(f[i-1],f[i]))%f[i],to=K/f[i-1]-1;
            if(a<to)
            {
                if(a==0) ans--;
                ans=(ans+1+(to-a)/f[i])%mod;
            }
        }
        printf("%lld",ans);
        return 0;
    }
    
  • 相关阅读:
    字符编码解码
    综合练习[购物车]
    for 循环实例
    数据类型
    字符串格式化输出
    集成开发环境
    while循环实例
    赋值运算符、逻辑运算符、表达式
    if,else语句猜最大值
    计算今天和今天的上一月的日期
  • 原文地址:https://www.cnblogs.com/jasony/p/13440367.html
Copyright © 2020-2023  润新知