几乎是抄的,啥都不要说了
贴上策爷和VFK的链接好了
http://jcvb.is-programmer.com/posts/39528.html
http://vfleaking.blog.163.com/blog/static/174807634201341721051604/
还是写两句吧。关键是将新数列每个元素模k后写出,在经减1余0的元素后面换行,并找规律
发现每行前两个数相同,因此每行第i个数为 Fibonacc[i]*行首的数,行末数为0,行长len[i]为:第一个模k等于[行首的数在模k下的逆元]的Fibonacc数的位置,每一行的后继行行首数字next[i]:行首的数*Fibonacc[len[i]-1]%k
把这些信息预处理出来会方便一些
还发现有几行会循环出现,考虑矩阵加速优化之:
从第1行开始,每行内部用矩阵加速处理
若n很大,进入循环,先处理一个循环节,然后在循环节之间也用矩阵加速处理
最后,不足一循环的,每行矩阵加速处理
注意特殊情况:不出现循环,即某一行的行首在模k下不存在逆元,或其逆元不等于任何Fibonacc[x]%k,此时设这一行无限长,无后继行
#include
#include
#include
typedef long long LL;
const LL INF=2000000000000000000ll;//结尾加"ll"表示long long型常量
LL len[1000005]={0};
int fib[6000005]={0},first[1000005]={0},next[1000005]={0},vis[1000005]={0};
LL mod;
struct juzhen
{
LL s[5][5];
juzhen()
{
memset(s,0,sizeof(s));
}
};
void exgcd(int a,int b,int& d,int& x,int& y)
{
int t;
if(b==0)
{
d=a;
x=1;
y=0;
return;
}
exgcd(b,a%b,d,x,y);
t=x;
x=y;
y=t-(a/b)*y;
}
int ni(int a,int n)
{
int d=0,x=0,y=0;
exgcd(a,n,d,x,y);
if(d!=1) return 0;
return (x+n)%n;
}
juzhen cheng(juzhen a,juzhen b)
{
juzhen res;
int i,j,k;
for(i=1;i<=3;i++)
for(j=1;j<=3;j++)
{
for(k=1;k<=3;k++)
res.s[i][j]+=a.s[i][k]*b.s[k][j];
res.s[i][j]%=mod;
}
return res;
}
juzhen ksm(juzhen a,LL n)
{
juzhen res;
res.s[1][1]=res.s[2][2]=res.s[3][3]=1;
if(n==0) return res;
if(n==1) return a;
res=ksm(a,n/2);
res=cheng(res,res);
if(n%2==1) res=cheng(res,a);
return res;
}
int main()
{
juzhen A,B,C,Z;
LL n,len_cir=0;
int k,i,cir;
scanf("%lld%d%lld",&n,&k,&mod);
fib[1]=fib[2]=1;
for(i=3;i==3||fib[i-1]!=1||fib[i-2]!=1;i++)//预处理出Fibonacc数列每个元素模k的值,直到出现循环
{
fib[i]=(fib[i-1]+fib[i-2])%k;
if(first[fib[i]]==0) first[fib[i]]=i;//记录余数最早出现的位置
}
for(i=1;i0)
{
do
{
C=cheng(C,ksm(A,len[i]));
C=cheng(C,B);
len_cir+=len[i];//计算圈上总共有多少项
i=next[i];
}
while(i!=cir);
Z=cheng(Z,ksm(C,n/len_cir));
n%=len_cir;
for(i=cir;;i=next[i])//剩下不完整圈的部分
{
if(n