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)*a\equiv 0~(mod~f(x))\\f(x-1)*a\equiv k~(mod~f(x))\\a\equiv 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;
}
posted @ 2020-08-05 15:47  jasony_sam  阅读(141)  评论(0编辑  收藏  举报