求斐波那契數列循環節

當斐波那契數列%mod時一定存在一個循環節,根據鴿巢原理,mod*(mod-1)一定是一個循環節,但不一定是最短的。
然後求斐波那契數列%n的循環節時,先把n質因數分解,
n=p1a1* p2a2*…*pkak
然後循環節就是每個pa 的循環節的最小公倍數,pa的循環節即爲num[p]*pow(p,a-1),num[p]是p的循環節。
即爲num[p1] pow(p1,a1-1)與num[p2] pow(p2,a2-1)與…與num[pk] pow(pk,ak-1)的最小公倍數。
num[]求法:只需要求出素數的num即可!
如果數比較小直接暴力即可,因爲最大就是mod * (mod-1),而且實際小很多,個人感覺跑幾千以內素數的num數組暴力是ok的。
如果數大的話,暴力枚舉求出fib[i]%mod==0的最小的i,記爲pos,令a=fib[i-1],然後計算出最小的x使得ax=1(%mod),答案即爲pos * x。

牛客 題中題
這個題打表找到是斐波那契數列,然後因爲需要的num很小,1000以內的質數即可,所以直接暴力的。我以爲得用__int128然而並沒有。

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
long long prime[1010];
int tot;
bool vis[1010];
void init(){
 int i,j;
 for(i=2;i<=1000;i++){
  if(vis[i])
  continue;
  prime[++tot]=i;
  for(j=i+i;j<=1000;j+=i)
  vis[j]=true;
 }
}
long long num[1010];
int a[501000];
long long gcd(long long a,long long b){
 return b?gcd(b,a%b):a;
}
int main(void){
 int i;
 long long n;
 init();
 num[1]=3;
 for(i=2;i<=tot;i++){
  a[1]=1;a[2]=2;
  int x=3;
  while(1){
   a[x]=a[x-1]+a[x-2];
   a[x]%=prime[i];
   if(a[x]==1&&a[x-1]==0)
   break;
   x++;
  }
  num[i]=x;
 }
 int T;
 cin>>T;
 while(T--){
  scanf("%lld",&n);
  long long ans=1;
  //cout<<num[1]<<" !!\n";
  for(i=1;i<=tot;i++){
   //cout<<n<<" "<<prime[i]<<"\n"; 
   if(n%prime[i]==0){
    long long x=0;
    while(n%prime[i]==0){
     n/=prime[i];
     x++;
    }
    long long k=num[i]*pow(prime[i],x-1);
    ans=ans/gcd(ans,k)*k;
   }
  }
  printf("%lld\n",ans);
 }
 return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章