NOIP模拟 k-斐波那契

【题目描述】

    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;
}

猜你喜欢

转载自blog.csdn.net/g21glf/article/details/82290499