hdu 3977 Evil teacher (斐波那契数列循环节)
题意:f[0] = 1,f[1] = 1,f[i] = f[i-1] + f[i-2] (i>=2),问最小的m满足f[n]%p==f[n+m]%p
思路:求斐波那契数列循环节。
参考了Acdreamer的博客_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。
那么我们可以先求出所有的因子,然后用矩阵快速幂来一个一个判断,这样时间复杂度不会很大。
这道题是模板题。博客中的模板代码很好理解...
换成了自己比较熟悉的矩阵构造方式,以及代码风格。
需要注意的是下标是从0开始的,当验证k是否为循环节的时候,应该验证f[k]和f[k+1]
/* ***********************************************
Author :111qqz
Created Time :Mon 31 Oct 2016 03:54:15 AM CST
File Name :code/hdu/3977.cpp
************************************************ */
1#include <cstdio>
2#include <cstring>
3#include <iostream>
4#include <algorithm>
5#include <vector>
6#include <queue>
7#include <set>
8#include <map>
9#include <string>
10#include <cmath>
11#include <cstdlib>
12#include <ctime>
13#define fst first
14#define sec second
15#define lson l,m,rt<<1
16#define rson m+1,r,rt<<1|1
17#define ms(a,x) memset(a,x,sizeof(a))
18typedef long long LL;
19#define pi pair < int ,int >
20#define MP make_pair
1using namespace std;
2const double eps = 1E-8;
3const int dx4[4]={1,0,0,-1};
4const int dy4[4]={0,-1,1,0};
5const int inf = 0x3f3f3f3f;
6LL P;
7struct Mat
8{
9 LL mat[2][2];
10 void clear()
11 {
12 ms(mat,0);
13 }
14}M,M1;
15Mat mul (Mat a,Mat b,LL mod)
16{
17 Mat c;
18 c.clear();
19 for ( int i = 0 ; i < 2 ; i++)
20 for ( int j = 0 ; j < 2 ; j++)
21 for ( int k = 0 ; k < 2 ; k++)
22 c.mat[i][j] = (c.mat[i][j] + a.mat[i][k] * b.mat[k][j]%mod)%mod;
23 return c;
24}
25Mat mat_ksm(Mat a,LL b,LL mod)
26{
27 Mat res;
28 res.clear();
29 for ( int i = 0 ; i < 2 ; i++) res.mat[i][i] = 1;
30 while (b>0)
31 {
32 if (b&1) res = mul(res,a,mod);
33 b = b >> 1LL;
34 a = mul(a,a,mod);
35 }
36 return res;
37}
38LL gcd(LL a,LL b)
39{
40 return b?gcd(b,a%b):a;
41}
42const int N = 1E6+7;
43bool prime[N];
44int p[N];
45void isprime() //一个普通的筛
46{
47 int cnt = 0 ;
48 ms(prime,true);
49 for ( int i = 2 ; i < N ; i++)
50 {
51 if (prime[i])
52 {
53 p[cnt++] = i ;
54 for ( int j = i+i ; j < N ; j+=i) prime[j] = false;
55 }
56 }
57}
58LL ksm( LL a,LL b,LL mod)
59{
60 LL res = 1;
61 while (b>0)
62 {
63 if (b&1) res = (res * a) % mod;
64 b = b >> 1LL;
65 a = a * a % mod;
66 }
67 return res;
68}
69LL legendre(LL a,LL p) //勒让德符号,判断二次剩余
70{
71 if (ksm(a,(p-1)>>1,p)==1) return 1;
72 return -1;
73}
74LL pri[N],num[N];//分解质因数的底数和指数。
75int cnt; //质因子的个数
76void solve(LL n,LL pri[],LL num[])
77{
78 cnt = 0 ;
79 for ( int i = 0 ; p[i] * p[i] <= n ; i++)
80 {
81 if (n%p[i]==0)
82 {
83 int Num = 0 ;
84 pri[cnt] = p[i];
85 while (n%p[i]==0)
86 {
87 Num++;
88 n/=p[i];
89 }
90 num[cnt] = Num;
91 cnt++;
92 }
93 }
94 if (n>1)
95 {
96 pri[cnt] = n;
97 num[cnt] = 1;
98 cnt++;
99 }
100}
101LL fac[N];
102int cnt2; //n的因子的个数
103void get_fac(LL n)//得到n的所有因子
104{
105 cnt2 = 0 ;
106 for (int i = 1 ; i*i <= n ; i++)
107 {
108 if (n%i==0)
109 {
110 if (i*i!=n)
111 {
112 fac[cnt2++] = i ;
113 fac[cnt2++] = n/i;
114 }
115 else fac[cnt2++] = i;
116 }
117 }
118}
119LL find_loop(LL n)
120{
121 solve(n,pri,num);
122 LL ans = 1;
123 for ( int i = 0 ; i < cnt ; i++)
124 {
125 LL rec = 1;
126 if (pri[i]==2) rec = 3;
127 else if (pri[i]==3) rec = 8;
128 else if (pri[i]==5) rec = 20;
129 else
130 {
131 if (legendre(5,pri[i])==1)
132 get_fac(pri[i]-1);
133 else get_fac(2*pri[i]+2);
134 sort(fac,fac+cnt2);
135 for ( int j = 0 ; j < cnt2 ; j++) //挨个验证因子
136 {
137 Mat tmp = mat_ksm(M,fac[j],pri[i]); //下标从0开始,验证fac[j]为循环节,应该看fib[0]==fib[fac[j]]和fib[1]==fib[fac[j]+1]是否成立
138 tmp = mul(tmp,M1,pri[i]);
139 if (tmp.mat[0][0]==1&&tmp.mat[1][0]==1)
140 {
141 rec = fac[j];
142 break;
143 }
144 }
1 }
2 for ( int j = 1 ; j < num[i] ; j++)
3 rec *=pri[i];
4 ans = ans / gcd(ans,rec) * rec;
5 }
6 return ans;
7}
8void init()
9{
10 M.clear();
11 M.mat[0][1] = M.mat[1][0] = M.mat[1][1] = 1;
12 M1.clear();
13 M1.mat[0][0] = M1.mat[1][0] = 1;
14}
15int main()
16{
17 #ifndef ONLINE_JUDGE
18 freopen("code/in.txt","r",stdin);
19 #endif
20 int T;
21 int cas = 0 ;
22 isprime();
23 scanf("%d",&T);
24 while (T--)
25 {
26 init();
27 scanf("%lld",&P);
28 printf("Case #%d: ",++cas);
29 LL ret = find_loop(P);
30 printf("%lld\n",ret);
31 }
1 #ifndef ONLINE_JUDGE
2 fclose(stdin);
3 #endif
4 return 0;
5}