求斐波那契数列循环节

当斐波那契数列%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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章