CodeForces 193E Fibonacci Number

190 篇文章 2 订阅
48 篇文章 0 订阅

题面

题意

给出一个数n,问是否存在一个斐波那契数mod1e13后等于n,若不存在输出-1,否则输出它是第几个。

做法

首先找一下规律,发现斐波那契数在模10,100,1000,1000……下的循环节大小分别为60,300,1500,15000,150000…….并且发现后来的循环节大小每次*10。
因此我们可以考虑先暴力求出模1e5(这个范围定为1e3,1e4也行)下与给出数同余的所有数,然后将模数不断*10(直至1e13),每次模数M乘10后,与给出数同余的数一定是由,在模M/10意义下同余的数加上k(0<=k<=9)个循环节后得到的,因此在模M时,可以暴力枚举模(M/10)时同余的数加上k(0<=k<=9)个当前循环节后的数,用矩阵快速幂检验是否在模M下同余即可。
而需要注意的是,在矩阵快速幂时,因为涉及到1e13以内的数的乘法,会爆long long,所以要像乘法快速幂那样来做。

inline ll cheng(ll u,ll v)
{
    ll res=0;
    for(; v; v>>=1)
    {
        if(v&1) res=(res+u)%M;
        u=u*2%M;
    }
    return res;
}

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
#define MAXM (ll)1e13
#define BJ 100000
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;

ll n,m,a,b=1,c,can[BJ*10],cc,M,tmp[BJ*10],ans=INF,XH;
inline ll cheng(ll u,ll v)
{
    ll res=0;
    for(; v; v>>=1)
    {
        if(v&1) res=(res+u)%M;
        u=u*2%M;
    }
    return res;
}
struct Jz
{
    ll num[2][2];
    Jz()
    {
        memset(num,0,sizeof(num));
    }
    Jz operator * (const Jz &u) const
    {
        Jz res;
        ll i,j,k;
        for(i=0; i<2; i++)
        {
            for(j=0; j<2; j++)
            {
                for(k=0; k<2; k++)
                {
                    res.num[i][j]=(res.num[i][j]+cheng(num[i][k],u.num[k][j]))%M;
                }
            }
        }
        return res;
    }
} dw;

inline Jz po(Jz u,ll v)
{
    Jz res;
    res.num[0][0]=res.num[1][1]=1;
    for(; v;)
    {
        if(v&1) res=res*u;
        u=u*u;
        v>>=1;
    }
    return res;
}

inline ll ask(ll u)
{
    Jz res=po(dw,u);
    return res.num[0][0];
}

int main()
{
    dw.num[0][1]=dw.num[1][0]=dw.num[1][1]=1;
    ll i,j,k,t;
    cin>>n;
    t=n%BJ;
    for(i=1;; i++)
    {
        if(t==a) can[++cc]=i;
        c=(a+b)%BJ;
        a=b,b=c;
        if(!a&&b==1) break;
    }
    for(M=BJ*10,XH=150000; M<=MAXM; M*=10,XH*=10)
    {
        t=n%M,k=0;
        for(i=1; i<=cc; i++)
        {
            for(j=0; j<=9; j++)
            {
                if(ask(can[i]+XH*j)%M==t)
                {
                    tmp[++k]=can[i]+XH*j;
                }
            }
        }
        for(i=1; i<=k; i++) can[i]=tmp[i];
        cc=k;
    }
    if(!cc)
    {
        puts("-1");
        return 0;
    }
    for(i=1; i<=cc; i++) ans=min(ans,can[i]);
    cout<<ans-1;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值