测试地址:兔农
做法:这题太神了orz,在跪拜了大佬的题解,再自己调了很久之后终于A掉了……这真的是第一题吗?!
这题需要用到矩阵快速幂以及乘法逆元。
把要求的数列的第
i
项称为
Ansi={(Ansi−1+Ansi−2)%P(Ansi−1+Ansi−2−1)%P(Ansi−1+Ansi−2)%K≠1(Ansi−1+Ansi−2)%K=1
那么,令两个 3×3 矩阵 A,B 为:
A=⎛⎝⎜110100001⎞⎠⎟,B=⎛⎝⎜ 1 0−1010001⎞⎠⎟
可以看出,只需在行向量 (0 1 1) 上不断右乘矩阵 A 和
我们把原来的斐波那契数列的第 i 项称为
将数列 AnsimodK 分段后我们发现可以每次计算一个段,若这个段长度为 l ,就要在当前算出的矩阵上右乘
以下是本人(又臭又长的)代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long
using namespace std;
ll N,K,P,fib[6000010],ap[1000010]={0},next[1000010];
int len[1000010],vis[1000010]={0};
struct matrix {ll s[3][3];} now,M[70],A,B,Fst,Pr;
void init()
{
scanf("%lld%lld%lld",&N,&K,&P);
now.s[0][0]=1,now.s[0][1]=0,now.s[0][2]=0;
now.s[1][0]=0,now.s[1][1]=1,now.s[1][2]=0;
now.s[2][0]=0,now.s[2][1]=0,now.s[2][2]=1;
Fst=now,Pr=now;
A.s[0][0]=1,A.s[0][1]=1,A.s[0][2]=0;
A.s[1][0]=1,A.s[1][1]=0,A.s[1][2]=0;
A.s[2][0]=0,A.s[2][1]=0,A.s[2][2]=1;
B.s[0][0]=1,B.s[0][1]=0,B.s[0][2]=0;
B.s[1][0]=0,B.s[1][1]=1,B.s[1][2]=0;
B.s[2][0]=-1,B.s[2][1]=0,B.s[2][2]=1;
}
ll exgcd(ll a,ll b,ll &x,ll &y)
{
ll x0=1,y0=0,x1=0,y1=1;
while(b)
{
ll tmp,q;
q=a/b;
tmp=x0,x0=x1,x1=tmp-q*x1;
tmp=y0,y0=y1,y1=tmp-q*y1;
tmp=a,a=b,b=tmp%b;
}
x=x0,y=y0;
return a;
}
ll find_inverse(ll x)
{
if (x==0) return 0;
ll px,py,d=exgcd(x,K,px,py);
if (d>1) return 0;
else return (px%(K/d)+(K/d))%(K/d);
}
matrix mult(matrix a,matrix b)
{
matrix S;
memset(S.s,0,sizeof(S.s));
for(int i=0;i<=2;i++)
for(int j=0;j<=2;j++)
for(int k=0;k<=2;k++)
S.s[i][j]=(S.s[i][j]+a.s[i][k]*b.s[k][j])%P;
return S;
}
matrix power(ll x)
{
matrix S;
memset(S.s,0,sizeof(S.s));
for(int i=0;i<=2;i++) S.s[i][i]=1;
ll p=0;
while(x)
{
if (x&1) S=mult(S,M[p]);
x>>=1;p++;
}
return S;
}
int main()
{
init();
ll p=2;fib[0]=0,fib[1]=fib[2]=1;
do
{
fib[++p]=(fib[p-1]+fib[p-2])%K;
ap[fib[p]]=ap[fib[p]]?ap[fib[p]]:p;
}
while(fib[p-1]!=0||fib[p]!=1);
for(int i=0;i<K;i++)
{
ll inv=find_inverse(i);
if (inv&&ap[inv])
{
len[i]=ap[inv];
next[i]=(fib[len[i]-1]*i)%K;
}
else len[i]=-1,next[i]=-1;
}
ll d=0,a=1;bool flag=0;
M[0]=A;vis[1]=d;
for(int i=1;i<=69;i++) M[i]=mult(M[i-1],M[i-1]);
while(a!=-1&&len[a]!=-1&&d+len[a]<=N)
{
now=mult(now,power(len[a]));
now=mult(now,B);
d+=len[a];a=next[a];
if (vis[a]) {flag=1;break;}
else vis[a]=d;
}
if (flag)
{
ll pr=d-vis[a],fst,aa=a;
d=0;a=1;
while(d!=vis[aa])
{
Fst=mult(Fst,power(len[a]));
Fst=mult(Fst,B);
d+=len[a];a=next[a];
}
fst=d;
d=vis[aa];a=aa;
while(d!=pr+vis[aa])
{
Pr=mult(Pr,power(len[a]));
Pr=mult(Pr,B);
d+=len[a];a=next[a];
}
now=Fst;
M[0]=Pr;
for(int i=1;i<=69;i++) M[i]=mult(M[i-1],M[i-1]);
now=mult(now,power((N-vis[aa])/pr));
M[0]=A;
for(int i=1;i<=69;i++) M[i]=mult(M[i-1],M[i-1]);
d=fst+pr*((N-vis[aa])/pr);a=aa;
while(a!=-1&&len[a]!=-1&&d+len[a]<=N)
{
now=mult(now,power(len[a]));
now=mult(now,B);
d+=len[a];a=next[a];
}
now=mult(now,power(N-d));
}
else now=mult(now,power(N-d));
printf("%lld",((now.s[1][0]+now.s[2][0])%P+P)%P);
return 0;
}