題面
題意
給出一個數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;
}