我们知道Fibonacci数列,现在我们来求一个Fib数模n的循环节的长度。
对于一个正整数n,我们求Fib数模n的循环节的长度的方法如下:
(1)把n素因子分解,即
(2)分别计算Fib数模每个的循环节长度,假设长度分别是
(3)那么Fib模n的循环节长度
从上面三个步骤看来,貌似最困难的是第二步,那么我们如何求Fib模的循环节长度呢?
这里有一个优美的定理:Fib数模的最小循环节长度等于
,其中
表示Fib数模素数
的最小循环节长度。可以看出我们现在最重要的就是求
对于求我们利用如下定理:
如果5是模的二次剩余,那么循环节的的长度是
的因子,否则,循环节的长度是
的因子。
顺便说一句,对于小于等于5的素数,我们直接特殊判断,loop(2)=3,loop(3)=8,loop(5)=20。
那么我们可以先求出所有的因子,然后用矩阵快速幂来一个一个判断,这样时间复杂度不会很大。
模板代码:
#include <iostream> #include <string.h> #include <algorithm> #include <stdio.h> #include <math.h> using namespace std; typedef unsigned long long LL; const int M = 2; struct Matrix { LL m[M][M]; }; Matrix A; Matrix I = {1,0,0,1}; Matrix multi(Matrix a,Matrix b,LL MOD) { Matrix c; for(int i=0; i<M; i++) { for(int j=0; j<M; j++) { c.m[i][j] = 0; for(int k=0; k<M; k++) c.m[i][j] = (c.m[i][j]%MOD + (a.m[i][k]%MOD)*(b.m[k][j]%MOD)%MOD)%MOD; c.m[i][j] %= MOD; } } return c; } Matrix power(Matrix a,LL k,LL MOD) { Matrix ans = I,p = a; while(k) { if(k & 1) { ans = multi(ans,p,MOD); k--; } k >>= 1; p = multi(p,p,MOD); } return ans; } LL gcd(LL a,LL b) { return b? gcd(b,a%b):a; } const int N = 400005; const int NN = 5005; LL num[NN],pri[NN]; LL fac[NN]; int cnt,c; bool prime[N]; int p[N]; int k; void isprime() { k = 0; memset(prime,true,sizeof(prime)); for(int i=2; i<N; i++) { if(prime[i]) { p[k++] = i; for(int j=i+i; j<N; j+=i) prime[j] = false; } } } LL quick_mod(LL a,LL b,LL m) { LL ans = 1; a %= m; while(b) { if(b & 1) { ans = ans * a % m; b--; } b >>= 1; a = a * a % m; } return ans; } LL legendre(LL a,LL p) { if(quick_mod(a,(p-1)>>1,p)==1) return 1; else return -1; } void Solve(LL n,LL pri[],LL num[]) { cnt = 0; LL t = (LL)sqrt(1.0*n); for(int i=0; p[i]<=t; i++) { if(n%p[i]==0) { int a = 0; pri[cnt] = p[i]; while(n%p[i]==0) { a++; n /= p[i]; } num[cnt] = a; cnt++; } } if(n > 1) { pri[cnt] = n; num[cnt] = 1; cnt++; } } void Work(LL n) { c = 0; LL t = (LL)sqrt(1.0*n); for(int i=1; i<=t; i++) { if(n % i == 0) { if(i * i == n) fac[c++] = i; else { fac[c++] = i; fac[c++] = n / i; } } } } LL find_loop(LL n) { Solve(n,pri,num); LL ans=1; for(int i=0; i<cnt; i++) { LL record=1; if(pri[i]==2) record=3; else if(pri[i]==3) record=8; else if(pri[i]==5) record=20; else { if(legendre(5,pri[i])==1) Work(pri[i]-1); else Work(2*(pri[i]+1)); sort(fac,fac+c); for(int k=0; k<c; k++) { Matrix a = power(A,fac[k]-1,pri[i]); LL x = (a.m[0][0]%pri[i]+a.m[0][1]%pri[i])%pri[i]; LL y = (a.m[1][0]%pri[i]+a.m[1][1]%pri[i])%pri[i]; if(x==1 && y==0) { record = fac[k]; break; } } } for(int k=1; k<num[i]; k++) record *= pri[i]; ans = ans/gcd(ans,record)*record; } return ans; } void Init() { A.m[0][0] = 1; A.m[0][1] = 1; A.m[1][0] = 1; A.m[1][1] = 0; } int main() { LL n; Init(); isprime(); while(cin>>n) cout<<find_loop(n)<<endl; return 0; }
典型题目
题目一:http://acm.hdu.edu.cn/showproblem.php?pid=3977
题目二:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5075
题目三:http://acm.hdu.edu.cn/showproblem.php?pid=3978
题目四:http://acm.hdu.edu.cn/showproblem.php?pid=4291
上面的题目一和题目二基本上是模板题,没有什么可以说的。对于第三题和第四题,我们可以看出题目四实际上是
题目三的简单版,都是一层一层找循环节,我们找出每一层的循环节后即可解决。
下面给出题目三的代码:
#include <iostream> #include <string.h> #include <algorithm> #include <stdio.h> #include <math.h> using namespace std; typedef long long LL; const int M=2; struct Matrix { LL m[M][M]; }; Matrix per= {1,0,0,1}; Matrix multi(Matrix a,Matrix b,LL MOD) { Matrix c; int i,j,k; for(i=0; i<M; i++) { for(j=0; j<M; j++) { c.m[i][j]=0; for(k=0; k<M; k++) { c.m[i][j]+=a.m[i][k]*b.m[k][j]%MOD; } c.m[i][j]%=MOD; } } return c; } Matrix power(Matrix a,LL k,LL MOD) { Matrix ans=per,p=a; while(k) { if(k&1) { ans=multi(ans,p,MOD); k--; } k>>=1; p=multi(p,p,MOD); } return ans; } LL gcd(LL a,LL b) { return b? gcd(b,a%b):a; } LL quick_mod(LL a,LL b,LL m) { LL ans=1; a%=m; while(b) { if(b&1) { ans=ans*a%m; b--; } b>>=1; a=a*a%m; } return ans; } //勒让德符号 int legendre(int a,int p) { if(quick_mod(a,(p-1)>>1,p)==1) return 1; else return -1; } const int N=1000005; const int NN=50005; bool prime[N]; int p[N]; int num[NN],pri[NN]; int num1[NN],pri1[NN]; int arr[NN]; int loop[N]; int k,cnt,c; void isprime() { k=0; int i,j; memset(prime,true,sizeof(prime)); for(i=2; i<N; i++) { if(prime[i]) { p[k++]=i; for(j=i+i; j<N; j+=i) { prime[j]=false; } } } } void find(int n,int pri[],int num[]) { cnt=0; int t=(int)sqrt(1.0*n); for(int i=0; p[i]<=t; i++) { if(n%p[i]==0) { int a=0; pri[cnt]=p[i]; while(n%p[i]==0) { a++; n/=p[i]; } num[cnt]=a; cnt++; } } if(n>1) { pri[cnt]=n; num[cnt]=1; cnt++; } } void dfs(int dept,int product=1) { if(dept==cnt) { arr[c++]=product; return; } for(int i=0; i<=num1[dept]; i++) { dfs(dept+1,product); product*=pri1[dept]; } } int find_loop(int n) { find(n,pri,num); int cnt1=cnt; LL ans=1; for(int i=0; i<cnt1; i++) { c=0; int record=1; if(pri[i]==2) record=3; else if(pri[i]==3) record=8; else if(pri[i]==5) record=20; else { if(legendre(5,pri[i])==1) find(pri[i]-1,pri1,num1); else find(2*(pri[i]+1),pri1,num1); dfs(0,1); sort(arr,arr+c); for(int k=0; k<c; k++) { Matrix A; A.m[0][0]=1; A.m[0][1]=1; A.m[1][0]=1; A.m[1][1]=0; Matrix a=power(A,arr[k]-1,pri[i]); int x=(a.m[0][0]+a.m[0][1])%pri[i]; int y=(a.m[1][0]+a.m[1][1])%pri[i]; if(x==1&&y==0) { record=arr[k]; break; } } } for(int k=1; k<num[i]; k++) record*=pri[i]; ans=ans/gcd(ans,record)*record; } return ans; } void Solve(int p,int k) { loop[0]=p; for(int i=1; i<=k; i++) loop[i]=find_loop(loop[i-1]); } int work(int n,int k,int p) { int t=n; LL ret,MOD; Matrix ans; Matrix A; A.m[0][0]=1; A.m[0][1]=1; A.m[1][0]=1; A.m[1][1]=0; Solve(p,k); for(int i=k; i>=0; i--) { MOD=loop[i]; ans=power(A,t,MOD); ret=(ans.m[1][0]+ans.m[1][1])%MOD; t=ret; } return ret; } int main() { isprime(); int T,n,k,p,tt=1; scanf("%d",&T); while(T--) { scanf("%d%d%d",&n,&k,&p); printf("Case #%d: %d\n",tt++,work(n,k,p)); } return 0; }
题目:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1195
分析:由于本题数据特别多,开始一直TLE,后来发现矩阵乘法里面如果取模太多会导致速度变得很慢,以前POJ上
的一道题关于矩阵求和也是因为这个TLE,尽量减少一些取模,速度会有几十倍甚至几百倍的提升。
代码:
#include <iostream> #include <string.h> #include <algorithm> #include <stdio.h> #include <math.h> #include <map> using namespace std; typedef long long LL; const int M = 2; struct Matrix { LL m[M][M]; }; Matrix A; Matrix I = {1,0,0,1}; Matrix multi(Matrix a,Matrix b,LL MOD) { Matrix c; for(int i=0; i<M; i++) { for(int j=0; j<M; j++) { c.m[i][j] = 0; for(int k=0; k<M; k++) c.m[i][j] += a.m[i][k] * b.m[k][j]; c.m[i][j] %= MOD; } } return c; } Matrix power(Matrix a,LL k,LL MOD) { Matrix ans = I,p = a; while(k) { if(k & 1) { ans = multi(ans,p,MOD); k--; } k >>= 1; p = multi(p,p,MOD); } return ans; } int gcd(int a,int b) { return b? gcd(b,a%b):a; } const int N = 400005; const int NN = 5005; int num[NN],pri[NN]; int fac[NN]; bool flag[NN]; int c; bool prime[N]; int p[N]; int k; int cnt1; int pri1[NN],num1[NN]; void isprime() { k = 0; memset(prime,true,sizeof(prime)); for(int i=2; i<N; i++) { if(prime[i]) { p[k++] = i; for(int j=i+i; j<N; j+=i) prime[j] = false; } } } LL quick_mod(LL a,LL b,LL m) { LL ans = 1; a %= m; while(b) { if(b & 1) { ans = ans * a % m; b--; } b >>= 1; a = a * a % m; } return ans; } int legendre(int a,int p) { if(quick_mod(a,(p-1)>>1,p)==1) return 1; else return -1; } void Solve(int n,int pri[],int num[],int &cnt) { cnt = 0; int t = (int)sqrt(1.0*n); for(int i=0; p[i]<=t; i++) { if(n%p[i]==0) { int a = 0; pri[cnt] = p[i]; while(n%p[i]==0) { a++; n /= p[i]; } num[cnt] = a; cnt++; } } if(n > 1) { pri[cnt] = n; num[cnt] = 1; cnt++; } } void dfs(int dept,int cnt,LL product,int pri1[],int num1[]) { if(dept == cnt) { fac[c++] = product; return; } for(int i=0; i<=num1[dept]; i++) { dfs(dept+1,cnt,product,pri1,num1); product *= pri1[dept]; } } map<int,int> mp; LL find_loop(LL n) { int cnt = 0; Solve(n,pri,num,cnt); LL ans = 1; for(int i=0; i<cnt; i++) { int record=1; if(mp.find(pri[i]) != mp.end()) { record = mp[pri[i]]; goto Test; } if(pri[i]==2) record=3; else if(pri[i]==3) record=8; else if(pri[i]==5) record=20; else { if(legendre(5,pri[i])==1) { c = 0; Solve(pri[i]-1,pri1,num1,cnt1); dfs(0,cnt1,1,pri1,num1); } else { c = 0; Solve(2*(pri[i]+1),pri1,num1,cnt1); dfs(0,cnt1,1,pri1,num1); } sort(fac,fac+c); for(int r=0; r<c; r++) flag[r] = 1; for(int k=c-1; k >= 0; k--) { if(!flag[k]) continue; Matrix a = power(A,fac[k]-1,pri[i]); int x = (a.m[0][0]%pri[i]+a.m[0][1]%pri[i])%pri[i]; int y = (a.m[1][0]%pri[i]+a.m[1][1]%pri[i])%pri[i]; if(x==1 && y==0) { record = fac[k]; } else { for(int j=0; j<=k; j++) { if(fac[k] % fac[j] == 0) flag[j] = 0; } } } mp[pri[i]] = record; } Test: for(int k=1; k<num[i]; k++) record *= pri[i]; ans=ans/gcd(ans,record)*record; } return ans; } void Init() { A.m[0][0] = 1; A.m[0][1] = 1; A.m[1][0] = 1; A.m[1][1] = 0; } int main() { int T,n; Init(); mp.clear(); isprime(); scanf("%d",&T); while(T--) { scanf("%d",&n); LL ans = find_loop(n); printf("%I64d\n",ans); } return 0; }