【题目】
原题地址
给定一个
C
C
C和一个
P
P
P,求最小的
n
n
n使得
f
i
b
n
≡
C
(
mod
P
)
fib_n\equiv C(\text{mod } P)
fibn≡C(mod P)。
多组数据,
T
≤
100
,
11
≤
P
≤
2
×
1
0
9
T\leq 100,11\leq P\leq 2\times 10^9
T≤100,11≤P≤2×109,
P
P
P是一个素数,且个位数字为
1
1
1或
9
9
9
【解题思路】
一道算是模板的题目,不过我倒是看了好久。初中数学没学好
首先这个素数的性质就很奥妙重重,观察以后我们发现 5 5 5在模 P P P下是有二次剩余的,于是我们可以考虑直接用 f i b fib fib的通项来做。
设 x = 1 + 5 2 x=\frac {1+\sqrt 5} 2 x=21+5那么要求的就是最小的 n n n满足 1 5 ( x n − ( 1 − x ) n ) ≡ C ( mod P ) \frac {1} {\sqrt 5}(x^n-(\frac 1 {-x})^n)\equiv C(\text{mod } P) 51(xn−(−x1)n)≡C(mod P)。实际上就是解决一个长得像 a + 1 a = b a+\frac 1 a=b a+a1=b的问题,因为 a , b a,b a,b都已知,我们直接同乘 a a a然后解二元一次方程就可以求出 a a a。也就是说我们已经知道了 x n x^n xn,于是我们现在可以通过 BSGS \text{BSGS} BSGS来求出这个 n n n了。
复杂度 O ( T P ) O(T\sqrt P) O(TP)
【参考代码】(看着写的,自己不是很熟悉这方面)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int T,C,mod;
ll sq5,inv2,Y,K,ans,cur;
ll ppow(ll x,ll y,ll p)
{
ll res=1;x%=p;
for(;y;y>>=1,x=x*x%p)if(y&1)res=res*x%p;
return res;
}
namespace BSGS
{
map<ll,ll>mp;
ll calc(ll x,ll y,ll p,bool fg)
{
//printf("%lld %lld %lld %d\n",x,y,p,fg?1:0);
mp.clear();fg=!fg;
ll S=ceil(sqrt(p)),now=1;
for(int i=0;i<S;++i,now=now*x%p)
if(!mp.count(now)) mp[now]=i;
for(int i=0;i<=p;i+=S)
{
now=y*ppow(ppow(x,i,p),p-2,p)%p;
if(mp.count(now) && (((i+mp[now])&1)^fg)) return i+mp[now];
}
return -1;
}
}
namespace Cipolla
{
ll w,p;
struct cd
{
ll x,y;
cd(ll _x,ll _y):x(_x),y(_y){}
cd operator * (const cd&a)const{return cd((x*a.x+y*a.y%p*w)%p,(x*a.y+y*a.x)%p);}
};
cd qpow(cd x,ll y)
{
cd res=cd(1,0);
for(;y;y>>=1,x=x*x)if(y&1)res=res*x;
return res;
}
ll calc(ll x,ll P)
{
p=P;x%=p;
if(!x) return 0;
ll t=ppow(x,p-1>>1,p);
if(t^1) return -1;
for(;;)
{
t=rand()%P;
ll now=(t*t%mod+mod-x)%mod;
if(ppow(now,p-1>>1,p)==p-1) break;
}
w=(t*t%mod+mod-x)%mod;
cd A=cd(t,1);
return qpow(A,p+1>>1).x;
}
}
void calk(int id)
{
ll v1=(cur+K)*inv2%mod,v2=(cur+mod-K)%mod*inv2%mod;
//printf("%lld %lld\n",v1,v2);
ll X=BSGS::calc(Y,v1,mod,id);
//printf("%lld %lld\n",X,mod);
if(~X) ans=min(ans,X);
X=BSGS::calc(Y,v2,mod,id);
//printf("%lld\n",X);
if(~X) ans=min(ans,X);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("CC_FN.in","r",stdin);
freopen("CC_FN.out","w",stdout);
#endif
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&C,&mod);
ans=mod+1;inv2=mod+1>>1;sq5=Cipolla::calc(5,mod);
Y=(sq5+1)*inv2%mod;cur=sq5*C%mod;
//printf("%lld %lld %lld %lld\n",sq5,cur,inv2,Y);
K=Cipolla::calc((ll)5*C*C%mod+4,mod);
if(~K) calk(0);
K=Cipolla::calc((ll)5*C*C%mod+mod-4,mod);
if(~K) calk(1);
if(ans==mod+1) puts("-1");
else printf("%lld\n",ans);
}
return 0;
}