problem
设 F i F_i Fi 表示斐波那契数列:
{ F n = n , n ≤ 1 F n = F n − 1 + F n − 2 , o t h e r w i s e \begin{cases} F_n=n ,&n ≤ 1\\ F_n=F_{n-1}+F_{n-2}, &\mathrm{otherwise} \end{cases} {Fn=n,Fn=Fn−1+Fn−2,n≤1otherwise
给定 c c c 和 p p p,求满足 F n ≡ c ( m o d p ) F_n\equiv c\pmod p Fn≡c(modp) 的最小的 n n n。
保证 11 ≤ p ≤ 2 × 1 0 9 11\le p\le2\times10^9 11≤p≤2×109, p p p 是素数,且 p m o d 10 p\bmod 10 pmod10 是一个完全平方数。
solution
首先,斐波那契数列是有通项公式的:
F n = 1 5 ( ( 1 + 5 2 ) n − ( 1 − 5 2 ) n ) F_n=\frac1{\sqrt 5}\left((\frac{1+\sqrt 5}{2})^n-(\frac{1-\sqrt 5}{2})^n\right) Fn=51((21+5)n−(21−5)n)
那么设 φ = 1 + 5 2 \varphi=\frac{1+\sqrt 5}2 φ=21+5,那么 − 1 φ = 1 − 5 2 -\frac 1\varphi=\frac{1-\sqrt5}{2} −φ1=21−5,那么相当于是解:
1 5 ( φ n − ( − 1 φ ) n ) ≡ c ( m o d p ) φ n − ( − 1 ) n φ n ≡ 5 c ( m o d p ) \begin{aligned} \frac{1}{\sqrt 5}(\varphi^n-(-\frac 1 \varphi)^n)&\equiv c\pmod p \\\varphi^n-\frac{(-1)^n}{\varphi^n}&\equiv \sqrt5c\pmod p \end{aligned} 51(φn−(−φ1)n)φn−φn(−1)n≡c(modp)≡5c(modp)
又令 ϕ = φ n \phi=\varphi^n ϕ=φn,继续化简,有:
ϕ − ( − 1 ) n ϕ ≡ 5 c ( m o d p ) \phi-\frac{(-1)^n}{\phi}\equiv \sqrt5c\pmod p ϕ−ϕ(−1)n≡5c(modp)
然后用个求根公式就可以解出 ϕ \phi ϕ:
ϕ ≡ 5 c ± 5 c 2 + 4 ( − 1 ) n 2 ( m o d p ) \phi\equiv\frac{\sqrt 5c\pm\sqrt{5c^2+4(-1)^n}}{2} \pmod p ϕ≡25c±5c2+4(−1)n(modp)
那么用二次剩余就可以解 ϕ \phi ϕ 了,又由于 ϕ = φ n \phi=\varphi^n ϕ=φn,再套个 BSGS 解 n n n 即可。注意解得时候要特判 n n n 的奇偶性来解。
然后 5 5 5 是模 p p p 意义下的二次剩余,直接解就可以了。
由于 p p p 是素数,且 p m o d 10 p \bmod 10 pmod10 是完全平方数,所以 p = ± 1 ( m o d 5 ) p=\pm1\pmod 5 p=±1(mod5),那么 p 5 − 1 2 = 1 p^{\frac{5-1}2}=1 p25−1=1,即 p p p 是模 5 5 5 意义下的二次剩余。
根据二次互反律,设 p , q p,q p,q 是不同的奇素数,有:( p q ) ( q p ) = ( − 1 ) p − 1 2 q − 1 2 \left(\frac pq\right)\left(\frac qp\right)=(-1)^{\frac{p-1}2\frac{q-1}2} (qp)(pq)=(−1)2p−12q−1
其中 ( p q ) \left(\frac pq\right) (qp) 是勒让德符号。
那么把 5 5 5 和 p p p 代进上式,就不难发现 5 5 5 是模 p p p 意义下的二次剩余。
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int T,C,P,s5,U,V;
int add(int x,int y) {return (ll)x+y>=P?(ll)x+y-P:x+y;}
int dec(int x,int y) {return (ll)x-y< 0?(ll)x-y+P:x-y;}
int mul(int x,int y) {return (ll)x*y>=P?(ll)x*y%P:x*y;}
int power(int a,int b){
int ans=1;
for(;b;b>>=1,a=mul(a,a)) if(b&1) ans=mul(ans,a);
return ans;
}
int Inv(int x) {return power(x,P-2);}
void Max(int &x,int y) {if(x<y)x=y;}
void Min(int &x,int y) {if(x>y)x=y;}
namespace Cipolla{
int I,val;
struct num{
int x,y;
num(int x=0,int y=0):x(x),y(y){}
friend num operator*(const num &a,const num &b){
return num(add(mul(a.x,b.x),mul(val,mul(a.y,b.y))),add(mul(a.y,b.x),mul(a.x,b.y)));
}
friend num operator^(num a,int b){
num ans(1,0);
for(;b;b>>=1,a=a*a) if(b&1) ans=ans*a;
return ans;
}
};
int Sqrt(int n){
if(!n) return 0;
if(power(n,(P-1)>>1)==P-1) return -1;
while(1){
I=rand(),val=dec(mul(I,I),n);
if(power(val,(P-1)>>1)==P-1) break;
}
return (num(I,1)^((P+1)>>1)).x;
}
}
using Cipolla::Sqrt;
unordered_map<int,int>Hash;
int BSGS(int a,int b,int type){
Hash.clear();
int now=b,t=ceil(sqrt(P));
for(int i=0;i<t;++i) Hash[now]=i,now=mul(now,a);
now=1,a=power(a,t);
if(!a) return b?P+1:1;
for(int i=0,pos;i<=t;++i){
pos=(Hash.find(now)==Hash.end())?-1:Hash[now],now=mul(now,a);
if(pos>=0&&i*t-pos>=0&&((i*t-pos)&1)==type) return i*t-pos;
}
return P+1;
}
int main(){
scanf("%d",&T);
while(T--){
scanf("%d%d",&C,&P);
s5=Sqrt(5),U=mul(add(1,s5),Inv(2)),V=mul(C,s5);
int d1=add(mul(V,V),4),d2=dec(mul(V,V),4),ans=P+1;
if(!d1||power(d1,(P-1)>>1)==1){
int tmp=Sqrt(d1),val;
val=mul(add(V,tmp),Inv(2)),Min(ans,BSGS(U,val,0));
val=mul(dec(V,tmp),Inv(2)),Min(ans,BSGS(U,val,0));
}
if(!d2||power(d2,(P-1)>>1)==1){
int tmp=Sqrt(d2),val;
val=mul(add(V,tmp),Inv(2)),Min(ans,BSGS(U,val,1));
val=mul(dec(V,tmp),Inv(2)),Min(ans,BSGS(U,val,1));
}
printf("%d\n",ans==P+1?-1:ans);
}
return 0;
}