当斐波那契数列%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;
}