然后据说有一个很强的结论:斐波那契数列在模k意义下一定是以0 1 1……为开头的循环,并且循环节长度<=6*k(但也可能很长,比n大),因此不用担心算不完,只需要暴力递推记录每一位%k的f值和vis值即可
这样,我们的处理过程就是:递推记录vis数组和f数组->建立转移矩阵->计算每一行的转移矩阵->如果出现循环节,用矩阵乘法快速处理->将剩余的转移用A矩阵乘到C中
1 #include <cstdio>
2 #include <cstring>
3 #include <algorithm>
4 #include <cstdlib>
5 #include <ctime>
6 using namespace std;
7 typedef long long LL;
8 const int N=1000010;
9 LL n,k,p,vis[N],f[N*6],ni[N],len[N];
10 bool ext[N];
11 void exgcd(LL a,LL b,LL&x,LL &y,LL &d)
12 {
13 if(b==0)d=a,x=1,y=0;
14 else exgcd(b,a%b,y,x,d),y-=x*(a/b);
15 }
16 inline LL inv(LL a,LL mod)
17 {
18 LL d,x,y;exgcd(a,mod,x,y,d);
19 return d==1?(x+mod)%mod:-1;
20 }
21 struct marx
22 {
23 LL m[4][4];
24 marx(){}
25 inline LL *operator [](LL x){return m[x];}
26 inline void clear(){memset(m,0,sizeof(m));}
27 marx operator * (const marx &b) const
28 {
29 marx c;c.clear();
30 for(int k=1;k<=3;k++)
31 for(int i=1;i<=3;i++)
32 for(int j=1;j<=3;j++)
33 c.m[i][j]=(c.m[i][j]%p+m[i][k]%p*b.m[k][j]%p)%p;
34 return c;
35 }
36 }mat[N],A,B,C,ans;
37 inline marx poww(marx x,LL len)
38 {
39 marx ret;ret.clear();
40 ret[1][1]=ret[2][2]=ret[3][3]=1;
41 while(len)
42 {
43 if(len&1)ret=ret*x;
44 len>>=1;x=x*x;
45 }
46 return ret;
47 }
48 int main()
49 {
50 scanf("%lld%lld%lld",&n,&k,&p);
51 f[1]=f[2]=1;
52 for(int i=3;;i++)
53 {
54 f[i]=(f[i-1]+f[i-2])%k;
55 if(!vis[f[i]])vis[f[i]]=i;
56 if(f[i]==f[i-1]&&f[i]==1)break;
57 }
58 A[1][1]=A[1][2]=A[2][1]=A[3][3]=1;
59 B[1][1]=B[2][2]=B[3][3]=1,B[3][1]=-1;
60 ans[1][2]=ans[1][3]=1;
61 LL x=1;bool flag=0;
62 while(n)
63 {
64 if(!ni[x])ni[x]=inv(x,k);
65 if(ni[x]==-1)ans=ans*poww(A,n),n=0;
66 else
67 {
68 if(!ext[x]||flag)
69 {
70 ext[x]=1;
71 if(!vis[ni[x]])
72 ans=ans*poww(A,n),n=0;
73 else
74 {
75 len[x]=vis[ni[x]];
76 if(n>=len[x])
77 {
78 n-=len[x];
79 mat[x]=poww(A,len[x])*B;
80 ans=ans*mat[x],x=x*f[len[x]-1]%k;
81 }
82 else ans=ans*poww(A,n),n=0;
83 }
84 }
85 else
86 {
87 LL cnt=0;
88 C.clear();C[1][1]=C[2][2]=C[3][3]=1;
89 for(LL i=x*f[len[x]-1]%k;i!=x;i=i*f[len[i]-1]%k)
90 cnt+=len[i],C=C*mat[i];
91 cnt+=len[x],C=mat[x]*C;
92 ans=ans*poww(C,n/cnt);
93 n%=cnt,flag=1;
94 }
95 }
96 }
97 printf("%lld",(ans[1][1]%p+p)%p);
98 }