求解斐波那契數列模$p$意義下最短循環節


如題,畢克老師給我們出的noip(NOIplus)模擬賽的\(Day1T1\)

首先我們知道斐波那契數列的特征根

\[\phi_1=\frac{1+\sqrt{5}}{2} \]

\[\phi_2=\frac{1-\sqrt{5}}{2} \]

於是

\[F_n=\frac{\phi_1^n-\phi_2^n}{\sqrt{5}} \]

對於不為\(5\)的質數\(p\)

\(5\)是模\(p\)意義下的二次剩余

設最小循環節長度為\(m\)
那么\(\phi_1,\phi_2\)是模\(p\)的完全剩余系中的元素
根據費馬小定理

\[\phi_1^{p-1} \equiv 1 \mod p​ \]

\[\phi_2^{p-1} \equiv 1 \mod p​ \]

\[F_{p-1} \equiv \frac{\phi_1^{p-1}-\phi_2^{p-1}}{\sqrt{5}} \equiv 0 \mod p \]

\[F_{p} \equiv \frac{\phi_1^{p}-\phi_2^{p}}{\sqrt{5}} \equiv \frac{\phi_1-\phi_2}{\sqrt{5}} \equiv 1 \mod p \]

\[m | p-1 \]

\(5\)是模\(p\)意義下的二次非剩余

設最小循環節長度為\(m\)
根據歐拉判別准則

\[5^{\frac{p-1}{2}} \equiv -1 \mod p \]

\[\phi_1^p \equiv (\frac{1+\sqrt{5}}{2})^p \equiv (\frac{1}{2})^p (1+\sqrt{5})^p \equiv (\frac{1}{2})^p (1+\sqrt{5}^p) \equiv \frac{1}{2} (1+5^{\frac{p-1}{2}} \sqrt{5}) \equiv \phi_2 \mod p \]

同理可得

\[\phi_2^p \equiv \phi_1 \mod p \]

\[F_{2p+1} \equiv \frac{\phi_1^{2p+1}-\phi_2^{2p+1}}{\sqrt{5}} \equiv \frac{\phi_1^{2p}\phi_1-\phi_2^{2p}\phi_2}{\sqrt{5}} \equiv \frac{\phi_2^{2}\phi_1-\phi_1^{2}\phi_2}{\sqrt{5}} \equiv \phi_1\phi_2\frac{\phi_2-\phi_1}{\sqrt{5}} \equiv 1 \mod p \]

\[F_{2p+2} \equiv \frac{\phi_1^2\phi_2^2-\phi_2^2\phi_1^2}{\sqrt{5}} \equiv 0 \mod p \]

\[F_{2p+3} \equiv F_{2p+1}+ F_{2p+2}\equiv 1 \mod p \]

\[m|2p+2 \]

對於質數的冪\(p^k\)

設模\(p\)意義下的最小循環節長度為\(m\),模\(p^k\)意義下的最小循環節長度為\(m'\)

\[F_m \equiv \frac{\phi_1^m-\phi_2^m}{\sqrt{5}} \equiv 0 \mod p \]

\[\phi_1^m \equiv \phi_2^m \mod p \]

\[F_{m+1} \equiv F_1 \mod p \]

\[\phi_1^{m+1}-\phi_2^{m+1}-\phi_1+\phi_2\equiv \phi_1(\phi_1^m-1)-\phi_2(\phi_2^m - 1) \equiv ( \phi_1-\phi_2)(\phi_1^m-1) \equiv 0 \mod p \]

\[\phi_1^m \equiv \phi_2^m \equiv 1 \mod p \]

\[{(\phi_1^m)}^{p^{k-1}} \equiv {(\phi_2^m)}^{p^{k-1}} \equiv 1 \mod p^k \]

注:這里引用了一個定理

\(a \equiv 1 \mod p\),則\(a^{p^k} \equiv 1 \mod p^{k+1}\)

可以用數學歸納法證明
\(k=1\)時,令\(a=np+1\)

\[a^p \equiv (np+1)^p \equiv \sum_{i=0}^{p}{C_p^i* (np)^p} \equiv 1+p*(np)+\sum_{i=2}^{p}{C_p^i* (np)^p} \equiv 1 \mod p^2 \]

若當\(k=m\)時成立,令\(a^{p^m}=np^{m+1}+1\)

\[a^{p^{m+1}} \equiv (a^{p^m})^p \equiv (np^{m+1}+1)^p \equiv 1+p*np^{m+1}+\sum_{i=2}^{p}{C_p^i *(np^{m+1})^p} \equiv 1 \mod p^{m+2} \]

得證

所以

\[F_{mp^{k-1}} \equiv 0 \mod p^k \]

\[F_{mp^{k-1}+1} \equiv 1 \mod p^k \]

\[m'|mp^{k-1} \]

注:
就我所知對於目前已知的所有情況,都有$ m'=mp^{k-1} $
然而我所能查到的論文都說數學上還沒有證明
如果哪位大佬知道請賜教……

對於合數\(p_1^{k_1}*p_2^{k_2}*...*p_c^{k_c}\)

設模\(p_1^{k_1}*p_2^{k_2}*...*p_c^{k_c}\)意義下的最小循環節長度為\(m\)

\[F_m \equiv 0 \mod p_1^{k_1}*p_2^{k_2}*...*p_c^{k_c} \]

\[F_{m+1} \equiv 1 \mod p_1^{k_1}*p_2^{k_2}*...*p_c^{k_c} \]

中國剩余定理

\[F_m \equiv 0 \mod p_1^{k_1} \]

\[F_{m+1} \equiv 1 \mod p_1^{k_1} \]

\[F_m \equiv 0 \mod p_2^{k_1} \]

\[F_{m+1} \equiv 1 \mod p_2^{k_1} \]

\[... \]

\[F_m \equiv 0 \mod p_c^{k_c} \]

\[F_{m+1} \equiv 1 \mod p_c^{k_c} \]

所以在模\(p_i^{k_i}\)意義下分別求解
答案取\(lcm\)即可

優化&總結&代碼

關於判斷\(5\)是否是模\(p\)(\(p\)為奇素數且\(p \neq 5\))意義下的二次剩余
可以直接用歐拉判別准則
但是需要快速冪,帶一個\(log\)
我們還可以考慮優化這個算法
二次互反律
可以得到

\[(\frac{5}{p})=(\frac{p}{5})(-1)^{\frac{(5-1)(p-1)}{4}}=(\frac{p}{5}) \]

因為

\[(\frac{1}{5}) = (\frac{4}{5}) =1 \]

\[(\frac{2}{5}) = (\frac{3}{5}) =-1 \]

所以
當且僅當\(p \equiv 1 \mod 5\)\(p \equiv 4 \mod 5\)\(5\)是模\(p\)意義下的二次剩余
當且僅當\(p \equiv 2 \mod 5\)\(p \equiv 3 \mod 5\)\(5\)是模\(p\)意義下的二次非剩余

\(2\)\(5\)比較特殊
同樣由二次互反律可以注意到\(2\)的條件是相反的
\(5\)則不在以上所有討論范圍內,沒有理論上的證明
然而不是有樣例嗎……
所以小於等於\(5\)的我都直接返回答案了(實際上\(1e6\)內都可以暴力跑出來)
對於其它質數求解時直接枚舉\(p-1\)\(2p+2\)的約數判斷即可

然后這道題就完美地做完了

#include<bits/stdc++.h>

using namespace std;

#define gc c=getchar()
#define r(x) read(x)
#define ll long long

template<typename T>
inline void read(T&x){
    x=0;T k=1;char gc;
    while(!isdigit(c)){if(c=='-')k=-1;gc;}
    while(isdigit(c)){x=x*10+c-'0';gc;}x*=k;
}

const int N=5e4+7;

int tot;
int pri[N];
bool mark[N];

inline void init(){
	for(int i=2;i<N;++i){
		if(!mark[i])pri[++tot]=i;
		for(int j=1,tmp;j<=tot&&(tmp=i*pri[j])<N;++j){
			mark[tmp]=1;
			if(i%pri[j]==0)break;
		}
	}
}

int p;

inline int add(int a,int b){
	a+=b;
	if(a>=p)a-=p;
	return a;
}

inline int mul(int a,int b){
	return (ll)a*b%p;
}

inline int spr(int a){
	return mul(a,a);
}

ll gcd(ll a,ll b){
	return b?gcd(b,a%b):a;
}

ll lcm(ll a,ll b){
	return a/gcd(a,b)*b;
}

map<int,int>F;

int fib(int n){
	if(n<=1)return n;
	int &ans=F[n];
	if(ans)return ans;
	if(n&1)return ans=add(spr(fib((n+1)>>1)),spr(fib((n-1)>>1)));
	int tmp=fib(n>>1);
	return ans=mul(tmp,add(mul(2,fib((n>>1)-1)),tmp));
}

vector<int> d;

inline void div(int x){
	int t=sqrt(x);
	d.clear();
	for(int i=2;i<t;++i){
		if(x%i==0){
			d.push_back(i);
			d.push_back(x/i);
		}
	}
	if(t*t==x)d.push_back(t);
	sort(d.begin(), d.end());
}

inline int check(int x){
	div(x);
	F.clear();
	for(int i=0;i<d.size();++i){
		if(fib(d[i])==0&&fib(d[i]+1)==1)return d[i];
	}
//	assert(fib(x)==0&&fib(x+1)==1);
	return x;

}

inline int query(int x){
	if(x==2)return 3;
	if(x==3)return 8;
	if(x==5)return 20;
	p=x;
	if(x%5==1||x%5==4)return check(x-1);
	if(x%5==2||x%5==3)return check(2*x+2);
//	assert(0);
}

inline ll solve(int n){
	ll ans=1;
	for(int i=1;i<=tot;++i){
		if(pri[i]>n)break;
		if(n%pri[i]==0){
			n/=pri[i];
			ll t=1;
			while(n%pri[i]==0){
				n/=pri[i];
				t*=pri[i];
			}
			ans=lcm(ans,t*query(pri[i]));
		}
	}
	if(n>1){
		ans=lcm(ans,query(n));
	}
	return ans;
}

int main(){
    init();
    int T;
    for(r(T);T;--T){
    	int n;r(n);
    	printf("%lld\n",solve(n));
    }
}

后記&其它做法

畢老師:“其實我只是想考察大家的找規律技巧,noip不會考證明的”
我:“其實我也只是自己感興趣而已”

如果感興趣可以看看這個網站以及它推薦的其它網站
OEIS也還行
我覺得在網上花些時間認真看論文還是有收獲的,遺憾的是幾乎沒有中文資料,所以自己來寫一寫
學信息學競賽就是這點好,有足夠的時間和充足的資料(雖然大部分是英語的)可以自己去研究,而不是被別人牽着走
說實話我覺得憑興趣和愛好去學習是一件很幸福的事,即使繞彎路,碰壁都會有收獲,都會感到快樂

另外這道題還可以用BSGS水過去
雖然沒有仔細看證明,有結論是答案不會超過\(6*p\)
然后大概就像這道題一樣搞搞就好了


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM