P3986 斐波那契数列(拓展欧几里得定理)

P3986 斐波那契数列

题目描述

定义一个数列: f ( 1 ) = a , f ( 2 ) = b , f ( n   ( n > 1 )   ) = f ( n − 1 ) + f ( n − 2 ) f(1)=a,f(2)=b,f(n\:(n>1)\:)=f(n-1)+f(n-2) f(1)=a,f(2)=b,f(n(n>1))=f(n1)+f(n2),其中 a , b a,b a,b均为正整数,问有多少种 ( a , b ) (a,b) (a,b)使得 K K K出现在数列中且不是前两项.

输出需要 m o d   1 e 9 + 7 mod \:1e9+7 mod1e9+7.

题目分析

我们由题目条件可以得到若干个等式

f j + f j + 1 = K ( j > = 0 ) f_j+f_{j+1}=K(j>=0) fj+fj+1=K(j>=0)

我们要求的就是使得上面这个等式被满足的 f 0 , f 1 f_0,f_1 f0,f1的数量.


斐波那契数列

由斐波那契数列的矩阵形式我们可以知道,斐波那契数列的第 i i i项可以如是表示

[ f n   f n − 1   ] ∗ [ 1 1 1 0 ] = [ f n + 1   f n   ] \begin{bmatrix} f_n\ f_{n-1}\ \end{bmatrix}*\begin{bmatrix} 1 & 1\\ 1 & 0 \end{bmatrix}=\begin{bmatrix} f_{n+1}\ f_n\ \end{bmatrix} [fn fn1 ][1110]=[fn+1 fn ]

也就是

[ f 1   f 0   ] ∗ [ 1 1 1 0 ] n − 1 = [ f n   f n − 1   ] \begin{bmatrix} f_1\ f_{0}\ \end{bmatrix}*\begin{bmatrix} 1 & 1\\ 1 & 0 \end{bmatrix}^{n-1}=\begin{bmatrix} f_{n}\ f_{n-1}\ \end{bmatrix} [f1 f0 ][1110]n1=[fn fn1 ]

也就是说,斐波那契数列的第 i i i项只与第0项和第1项有关,设 f 0 = 1 , f 1 = 1 f_0=1,f_1=1 f0=1,f1=1的斐波那契数列第 i i i项为 f i f_i fi f 0 = a , f 1 = b f_0=a,f_1=b f0=a,f1=b的斐波那契数列第 i i i项为 g i g_i gi,那么我们可以得到

g i = f i − 2 ∗ a + f i − 1 ∗ b g_i=f_{i-2}*a+f_{i-1}*b gi=fi2a+fi1b

现在,问题就转化成了求满足不定方程 K = f i − 2 ∗ a + f i − 1 ∗ b K=f_{i-2}*a+f_{i-1}*b K=fi2a+fi1b的方案数.

首先发现 f i − 1 , f i − 2 f_{i-1},f_{i-2} fi1,fi2发现这个东西显然可以枚举,因为斐波那契数列小于等于 1 e 9 1e9 1e9的项只有大约 40 40 40项左右.

那么我们就枚举斐波那契数列的第 i ( i < = 40 ) i(i<=40) i(i<=40)项,然后对于构成的不定方程分别求合法方案数即可.


求不定方程的特殊解:要求不定方程的合法解,首先要求出不定方程的一组通解.

我们有一个结论:斐波那契数列的任相邻两项都是互质的(窝不会证)

所以对于每一个不定方程 K = f i − 2 ∗ a + f i − 1 ∗ b K=f_{i-2}*a+f_{i-1}*b K=fi2a+fi1b,都一定能求出通解 ( a 1 , b 1 ) (a_1,b_1) (a1,b1).

实际上就算不知道这个结论,在程序里多写一个特判同样没有问题.

不定方程的通解怎么求?当然是用拓展欧几里得.

A = f i − 2 , B = f i − 1 , X = a , Y = b A=f_{i-2},B=f_{i-1},X=a,Y=b A=fi2,B=fi1,X=a,Y=b ,我们要做的就是先求出不定方程 1 = A X + B Y 1=AX+BY 1=AX+BY的一组解 ( X , Y ) (X,Y) (X,Y),那么 a , b a,b a,b的一组特殊解 ( a 1 , b 1 ) = ( K ∗ X , K ∗ Y ) (a_1,b_1)=(K*X,K*Y) (a1,b1)=(KX,KY)

关于不定方程的一组解:假设我们求出了不定方程 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b)的一组解 ( x 1 , y 1 ) (x_1,y_1) (x1,y1)
那么对于一个不定方程 a x + b y = K , K ∣ g c d ( a , b ) ax+by=K,K|gcd(a,b) ax+by=K,Kgcd(a,b),设 w = K / g c d ( a , b ) w=K/gcd(a,b) w=K/gcd(a,b),那么这个不定方程同样有一组解 ( x 1 ∗ w , x 2 ∗ w ) (x_1*w,x_2*w) (x1w,x2w)


求不定方程通解&通解数量:求出了一组解之后,我们要做的就是求出通解的表示方式. 而要求这个表示方式,自然就要先求通解间隔.

我们现在求出了不定方程 a x + b y = k ax+by=k ax+by=k的一组特殊解 ( x 1 , y 1 ) (x_1,y_1) (x1,y1).

假设对于 x x x的通解间隔为 p p p,对于 y y y的通解间隔为 q q q,那么我们显然有

a ( x 1 + p ) + b ( y 1 + q ) = k a(x_1+p)+b(y_1+q)=k a(x1+p)+b(y1+q)=k

联立 a x 1 + b y 1 = k ax_1+by_1=k ax1+by1=k

a p = − b q ap=-bq ap=bq

也就是

p = − b a ∗ q p=-\frac{b}{a}*q p=abq

由于 p , q p,q p,q都是整数, g c d ( a , b ) = 1 gcd(a,b)=1 gcd(a,b)=1,所以对于 y y y的最小通解间隔为 q = a q=a q=a.

同理对于 x x x的最小通解间隔为 p = b p=b p=b

也就是对于以上的不定方程, ( x 1 + b n , y 1 − a n ) (x_1+bn,y_1-an) (x1+bn,y1an)也是一组解(简记为“一减一加”).

对应在题目中, ( K ∗ X + f n − 2 ∗ n , K ∗ Y − f n − 1 ∗ n ) (K*X+f_{n-2}*n,K*Y-f_{n-1}*n) (KX+fn2n,KYfn1n)也是一组解.

而对于一组解 ( x , y ) (x,y) (x,y) x , y x,y x,y都要大于0(题目要求的正整数的条件). 我们只需要求出满足 ( x , y ) (x,y) (x,y)大于0的解的数量即可.


到这里整道题就做完了. 不要忘记取模.

另外,有没有可能出现同样的解 ( a , b ) (a,b) (a,b)出现了两次的情况呢?答案是否定的,因为这需要

a f i + b f i − 1 = a f j + b f j − 1 , i ! = j af_i+bf_{i-1}=af_j+bf_{j-1},i!=j afi+bfi1=afj+bfj1,i!=j

由于斐波那契数列任两项不相等,所以以上式子是不成立的.

程序实现

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll mod=1e9+7;
ll n,gcd,x,y,f[60],ans;
ll exgcd(ll a,ll b){
	if(!b){
		x=1,y=0;
		return a;
	}
	ll ret=exgcd(b,a%b);
	ll z=x;x=y;y=z-a/b*y;
	return ret;
}//求对于每个不定方程的特殊解
int main(){
	scanf("%lld",&n);
	f[0]=1,f[1]=1;
	for(int i=2;i<=50;i++){
		f[i]=f[i-1]+f[i-2];
	} //K不会超过斐波那契数列第40项
	for(int i=1;f[i]<n;i++){
		gcd=exgcd(f[i-1],f[i]);
		x*=n,y*=n;
		if(x<=0){
			ll dex=-x/f[i]+1;
			x+=dex*f[i],y-=dex*f[i-1];//(x,y)要一起进退
		}//如果x<=0,把它加到大于0为止
		if(y<=0){
			ll dex=-y/f[i-1]+1;
			y+=dex*f[i-1],x-=dex*f[i];
		}//同理
		if(x<=0||y<=0)continue;//如果处理之后仍然找不到解,说明无解
		ans+=(y%f[i-1]==0)?y/f[i-1]:y/f[i-1]+1;
		//加上最多能取的数量(减多少减到0,由于这边减,另外一边是加,所以另外一边不用管)
		ans%=mod;
		ans+=(x%f[i]==0)?x/f[i]-1:x/f[i];//注意判断整除的情况
		ans%=mod;
	}
	printf("%lld\n",ans);
	return 0;
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值