【题目描述】
k-斐波那契数列定义如下:
已知f[n]=1,求【0,P)内所有可能的K。
【输入描述】
一行两个整数,n和P。
【输出描述】
从小到大输出所有可能的K,如果没有则输出None。
【输入样例】
5 5
【输出样例】
2
【备注】
对于30%数据,n,P<=1000。
对于100%数据,n, P ≤ 10^9。
对于30%的数据,我们可以发现,f[0]=f[1]=k*1,f[2]=f[1]+f[0]=k*2,可以发现k的系数满足斐波那契数列,所以可以直接递推求出f[n]的k的系数,此时问题转换为解f[n]*k≡1(mod p),枚举k,复杂度为O(n*P)。
对于100%的数据,我们发现,对于斐波那契数列f[n]=f[n-1]+f[n-2],改写一下即为f[n]=f[n-1]*1+f[n-2]*1,发现这类似于矩阵乘法,基础矩阵base为{0,1,1,1},第一个矩阵为{1,0,0,1},这样通过矩阵乘法即可计算出fib[n],因为矩阵乘法具有结合律,所以我们可以利用快速幂的思想来解决求fib[n],将复杂度降为O(log n);求解不定方程时,可以利用扩展欧几里得来解决f[n]*k≡1(mod p),由于转化后为f[n]*k+t*p=1,所以任意两个合法k之间至少相差p,所以如果存在可行解,只要调整至[0,p)即可,如果gcd(f[n],p)!=1,那么原不定方程无解,输出None。
贴代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef LL arr[2][2];
arr base={0,1,1,1},ans={1,0,0,1},tmp;
LL n,P;
void mult(arr a,arr b,arr c)
{
LL i,j,k;
for(i=0;i<2;++i)
{
for(j=0;j<2;++j)
{
a[i][j]=0;
for(k=0;k<2;++k)
a[i][j]+=(LL)(b[i][k]*c[k][j])%P;
a[i][j]%=P;
}
}
}
bool exgcd(LL a,LL b,LL &x,LL &y)
{
if(b==0)
{
x=1,y=0;
return a!=1;
}
if(exgcd(b,a%b,y,x))
return true;
y-=a/b*x;
return false;
}
int main()
{
scanf("%lld%lld",&n,&P);
for(;n;n>>=1)
{
if(n&1)
{
memcpy(tmp,ans,sizeof(arr));
mult(ans,tmp,base);
}
memcpy(tmp,base,sizeof(arr));
mult(base,tmp,tmp);
}
LL x,y,fn=(ans[0][0]+ans[1][0])%P;
if(exgcd(fn,P,x,y))
printf("None");
else
printf("%lld\n",(x%P+P)%P);
return 0;
}