《GMOJ-Senior-1240 Fibonacci sequence》题解

题目大意

∑ i = x y f ( i ) m o d    10000 \sum_{i=x}^{y}f(i) \mod 10000 i=xyf(i)mod10000 f ( i ) = { f ( i − 1 ) + f ( i − 2 )   ,   ( i ≥ 3 ) 1   ,   ( i = 1 , 2 ) f(i)= \left \{^{1 \space , \space (i=1,2)}_{f(i-1)+f(i-2) \space , \space (i \geq 3)} \right. f(i)={f(i1)+f(i2) , (i3)1 , (i=1,2))。

分析

这是一道数论题。
显然, f ( i ) f(i) f(i)为斐波那契数列。因为
∑ i = x y f ( i ) m o d    10000 = [ ( ∑ i = 1 y f ( i ) m o d    10000 ) − ( ∑ i = 1 x − 1 f ( i ) m o d    10000 ) + 10000 ] m o d    10000 , \sum_{i=x}^{y}f(i) \mod 10000=[( \sum_{i=1}^{y}f(i) \mod 10000)- ( \sum_{i=1}^{x-1}f(i) \mod 10000)+10000] \mod 10000 \text{,} i=xyf(i)mod10000=[(i=1yf(i)mod10000)(i=1x1f(i)mod10000)+10000]mod10000
所以问题就被转化为求 ∑ i = 1 n f ( i ) m o d    10000 \sum_{i=1}^{n}f(i) \mod 10000 i=1nf(i)mod10000。如此一来,问题就有很多种解法了。

找规律

因为只是 m o d    10000 \mod 10000 mod10000,所以 ∑ i = 1 n f ( i ) m o d    10000 \sum_{i=1}^{n}f(i) \mod 10000 i=1nf(i)mod10000可能存在循环。我们可以先写一个程序,找一下 ∑ i = 1 n f ( i ) m o d    10000 \sum_{i=1}^{n}f(i) \mod 10000 i=1nf(i)mod10000的规律:

#include<cstdio>
int mod=10000;//模数
int main()
{
	printf("1");
	int a=1/*递推斐波那契数列用*/,b=1/*递推斐波那契数列用*/,s=2/*递推sum用*/;
	for(int i=3;i<=300000;i++)
	{
		int c=(a+b)%mod/*求出模意义下下一个斐波那契数列的数*/,ns=(s+c)%mod/*递推原式*/;
		if(s==1&&ns==2)//找到循环
		{
			printf(" %d",i-1);//输出
		}
		a=b;//更新
		b=c;
		s=ns;
	}
	return 0;
}

运行程序得(竖着看):

1 1 1 75001 75001 75001 150001 150001 150001 225001 225001 225001
15001 15001 15001 90001 90001 90001 165001 165001 165001 240001 240001 240001
30001 30001 30001 105001 105001 105001 180001 180001 180001 255001 255001 255001
45001 45001 45001 120001 120001 120001 195001 195001 195001 270001 270001 270001
60001 60001 60001 135001 135001 135001 210001 210001 210001 285001 285001 285001

我们可以发现,这些数都可以表示为 15000 n + 1 15000n+1 15000n+1 n n n为非负整数)。由此,我们可以知道, ∑ i = 1 n f ( i ) m o d    10000 = ∑ i = 1 n m o d    15000 f ( i ) m o d    10000 \sum_{i=1}^{n}f(i) \mod 10000= \sum_{i=1}^{n \mod 15000}f(i) \mod 10000 i=1nf(i)mod10000=i=1nmod15000f(i)mod10000。于是,我们只要求出 ∑ i = 1 k f ( i ) m o d    10000 \sum_{i=1}^{k}f(i) \mod 10000 i=1kf(i)mod10000 0 ≤ k < 15000 0 \leq k<15000 0k<15000),输出
[ ( ∑ i = 1 y m o d    15000 f ( i ) m o d    10000 ) − ( ∑ i = 1 ( x − 1 ) m o d    15000 f ( i ) m o d    10000 ) + 10000 ] m o d    10000 [( \sum_{i=1}^{y \mod 15000}f(i) \mod 10000)-( \sum_{i=1}^{(x-1) \mod 15000}f(i) \mod 10000)+10000] \mod 10000 [(i=1ymod15000f(i)mod10000)(i=1(x1)mod15000f(i)mod10000)+10000]mod10000
的值就可以了。
代码如下:

#include<cstdio>
int mod=10000/*模数*/,f[15051]/*sum*/;
int main()
{
	int T;
	scanf("%d",&T);//读入数据组数
	f[0]=0;//初始化f
	f[1]=1;
	f[2]=2;
	int a=1,b=1;//递推斐波那契数列
	for(int i=3;i<15000;i++)
	{
		int c=(a+b)%mod;//求出模意义下下一个斐波那契数列的数
		f[i]=(f[i-1]+c)%mod;//递推f
		a=b;//更新
		b=c;
	}
	while(T--)//注意多组数据
	{
		int x,y;
		scanf("%d%d",&x,&y);//读入x,y
		printf("%d\n",(f[y%15000]-f[(x-1)%15000]+mod)%mod);//输出
	}
	return 0;
}
矩阵快速幂

我们想到可以用矩阵快速幂求出斐波那契数列的任意一项,那么我们能否用矩阵快速幂求出斐波那契数列前 n n n项的和呢?(注:这里不讨论取模的情况)

方法1

想要求出斐波那契数列前 n n n项的和,我们先要求出斐波那契数列的第 n n n项,于是我们要记录 f ( n − 1 ) , f ( n ) f(n-1),f(n) f(n1),f(n)。又因为要求和,所以我们还要记录一项 s u m n sum_n sumn表示 ∑ i = 1 n f ( i ) \sum_{i=1}^{n}f(i) i=1nf(i)。由此我们得到一个 3 × 1 3 \times 1 3×1的矩阵 [ f ( n − 1 ) f ( n ) s u m n ] \begin{bmatrix} f(n-1) \\ f(n) \\ sum_n \end{bmatrix} f(n1)f(n)sumn。那我们要怎么从 [ f ( n − 1 ) f ( n ) s u m n ] \begin{bmatrix} f(n-1) \\ f(n) \\ sum_n \end{bmatrix} f(n1)f(n)sumn推出 [ f ( n ) f ( n + 1 ) s u m n + 1 ] \begin{bmatrix} f(n) \\ f(n+1) \\ sum_{n+1} \end{bmatrix} f(n)f(n+1)sumn+1呢?显然要被一个 3 × 3 3 \times 3 3×3的矩阵乘。那么怎么推出这个矩阵呢?
因为
[ a 1 , 1 a 1 , 2 a 1 , 3 a 2 , 1 a 2 , 2 a 2 , 3 a 3 , 1 a 3 , 2 a 3 , 3 ] × [ b 1 b 2 b 3 ] = [ a 1 , 1 × b 1 + a 1 , 2 × b 2 + a 1 , 3 × b 3 a 2 , 1 × b 1 + a 2 , 2 × b 2 + a 2 , 3 × b 3 a 3 , 1 × b 1 + a 3 , 2 × b 2 + a 3 , 3 × b 3 ] , \begin{bmatrix} a_{1,1} & a_{1,2} & a_{1,3} \\ a_{2,1} & a_{2,2} & a_{2,3} \\ a_{3,1} & a_{3,2} & a_{3,3} \end{bmatrix} \times \begin{bmatrix} b_1 \\ b_2 \\ b_3 \end{bmatrix}= \begin{bmatrix} a_{1,1} \times b_1+a_{1,2} \times b_2+a_{1,3} \times b_3 \\ a_{2,1} \times b_1+a_{2,2} \times b_2+a_{2,3} \times b_3 \\a_{3,1} \times b_1+a_{3,2} \times b_2+a_{3,3} \times b_3 \end{bmatrix} \text{,} a1,1a2,1a3,1a1,2a2,2a3,2a1,3a2,3a3,3×b1b2b3=a1,1×b1+a1,2×b2+a1,3×b3a2,1×b1+a2,2×b2+a2,3×b3a3,1×b1+a3,2×b2+a3,3×b3
所以我们可以通过 [ f ( n − 1 ) f ( n ) s u m n ] \begin{bmatrix} f(n-1) \\ f(n) \\ sum_n \end{bmatrix} f(n1)f(n)sumn [ f ( n ) f ( n + 1 ) s u m n + 1 ] \begin{bmatrix} f(n) \\ f(n+1) \\ sum_{n+1} \end{bmatrix} f(n)f(n+1)sumn+1的关系求出这个 3 × 3 3 \times 3 3×3的矩阵:

  1. f ( n − 1 ) f(n-1) f(n1) f ( n ) f(n) f(n):因为 f ( n ) f(n) f(n) [ f ( n − 1 ) f ( n ) s u m n ] \begin{bmatrix} f(n-1) \\ f(n) \\ sum_n \end{bmatrix} f(n1)f(n)sumn中的第二行已经出现了,我们可以直接用,所以 a 1 , 1 = 0 , a 1 , 2 = 1 , a 1 , 3 = 0 a_{1,1}=0,a_{1,2}=1,a_{1,3}=0 a1,1=0,a1,2=1,a1,3=0
  2. f ( n ) f(n) f(n) f ( n + 1 ) f(n+1) f(n+1):因为 f ( n + 1 ) = f ( n − 1 ) + f ( n ) f(n+1)=f(n-1)+f(n) f(n+1)=f(n1)+f(n),而 f ( n − 1 ) f(n-1) f(n1)出现在 [ f ( n − 1 ) f ( n ) s u m n ] \begin{bmatrix} f(n-1) \\ f(n) \\ sum_n \end{bmatrix} f(n1)f(n)sumn的第一行, f ( n ) f(n) f(n)出现在 [ f ( n − 1 ) f ( n ) s u m n ] \begin{bmatrix} f(n-1) \\ f(n) \\ sum_n \end{bmatrix} f(n1)f(n)sumn的第二行,所以 a 2 , 1 = 1 , a 2 , 2 = 1 , a 2 , 3 = 0 a_{2,1}=1,a_{2,2}=1,a_{2,3}=0 a2,1=1,a2,2=1,a2,3=0
  3. s u m n sum_n sumn s u m n + 1 sum_{n+1} sumn+1:因为 s u m n + 1 = s u m n + f ( n + 1 ) sum_{n+1}=sum_n+f(n+1) sumn+1=sumn+f(n+1),而 f ( n + 1 ) f(n+1) f(n+1)的表示已经在上面得出,所以 a 3 , 1 = 1 , a 3 , 2 = 1 , a 3 , 3 = 1 a_{3,1}=1,a_{3,2}=1,a_{3,3}=1 a3,1=1,a3,2=1,a3,3=1

于是,这个 3 × 3 3 \times 3 3×3的矩阵就被求出来了: [ 0 1 0 1 1 0 1 1 1 ] \begin{bmatrix} 0 & 1 & 0 \\ 1 & 1 & 0 \\ 1 & 1 &1 \end{bmatrix} 011111001。有了这个矩阵,我们就可以用矩阵快速幂求出结果了。我们令 f ( 0 ) = 0 f(0)=0 f(0)=0,那么可得
∑ i = 0 n f ( i ) = [ 0 1 0 1 1 0 1 1 1 ] n − 1 × [ f ( 0 ) f ( 1 ) s u m 1 ] 所得最后一项 = [ 0 1 0 1 1 0 1 1 1 ] n − 1 × [ 0 1 1 ] 所得最后一项。 \sum_{i=0}^{n}f(i)=\begin{bmatrix} 0 & 1 & 0 \\1 & 1 & 0 \\ 1 & 1 & 1 \end{bmatrix}^{n-1} \times \begin{bmatrix} f(0) \\ f(1) \\ sum_1 \end{bmatrix} \text{所得最后一项}=\begin{bmatrix} 0 & 1 & 0 \\1 & 1 & 0 \\ 1 & 1 & 1 \end{bmatrix}^{n-1} \times \begin{bmatrix} 0 \\ 1 \\ 1 \end{bmatrix} \text{所得最后一项。} i=0nf(i)=011111001n1×f(0)f(1)sum1所得最后一项=011111001n1×011所得最后一项。
因为 f ( 0 ) = 0 f(0)=0 f(0)=0,所以
∑ i = 1 n f ( i ) = ∑ i = 0 n f ( i ) = [ 0 1 0 1 1 0 1 1 1 ] n − 1 × [ 0 1 1 ] 所得最后一项。 \sum_{i=1}^{n}f(i)=\sum_{i=0}^{n}f(i)=\begin{bmatrix} 0 & 1 & 0 \\1 & 1 & 0 \\ 1 & 1 & 1 \end{bmatrix}^{n-1} \times \begin{bmatrix} 0 \\ 1 \\ 1 \end{bmatrix} \text{所得最后一项。} i=1nf(i)=i=0nf(i)=011111001n1×011所得最后一项。
同时我们只要特判一下 n = 0 n=0 n=0的情况(结果为 0 0 0)就好了。
如何进行矩阵快速幂呢?和数的快速幂类似,我们只要把数的乘法换成矩阵的乘法即可。如何进行矩阵的乘法呢?对于一个 n × m n \times m n×m的矩阵 A A A和一个 m × k m \times k m×k的矩阵 B B B A × B A \times B A×B所得的矩阵 C C C n × k n \times k n×k的,且 C i , j = ∑ t = 1 m ( A i , t × B t , j ) C_{i,j}=\sum_{t=1}^{m}(A_{i,t} \times B_{t,j}) Ci,j=t=1m(Ai,t×Bt,j)。(此处 1 ≤ i ≤ n , 1 ≤ j ≤ k 1 \leq i \leq n,1 \leq j \leq k 1in,1jk
解题的代码如下:

#include<cstdio>
int mod=10000;//模数(注意取模)
struct matrix{int data[4][4];};//矩阵
matrix mul(matrix a,matrix b)//矩阵乘法
{
	matrix ans;
	for(int i=1;i<=3;i++)
	{
		for(int j=1;j<=3;j++)
		{
			ans.data[i][j]=0;
			for(int k=1;k<=3;k++)
			{
				ans.data[i][j]+=a.data[i][k]*b.data[k][j]%mod;
				ans.data[i][j]%=mod;
			}
		}
	}
	return ans;
}
int fsum(int n)
{
	if(n==0)//特判n=0
	{
		return 0;
	}
	matrix a/*推出的矩阵*/,ans/*最终矩阵*/;
	a.data[1][1]=0;//初始化a(推出的3*3矩阵)
	a.data[1][2]=1;
	a.data[1][3]=0;
	a.data[2][1]=1;
	a.data[2][2]=1;
	a.data[2][3]=0;
	a.data[3][1]=1;
	a.data[3][2]=1;
	a.data[3][3]=1;
	ans.data[1][1]=1;//初始化ans(单位矩阵)
	ans.data[1][2]=0;
	ans.data[1][3]=0;
	ans.data[2][1]=0;
	ans.data[2][2]=1;
	ans.data[2][3]=0;
	ans.data[3][1]=0;
	ans.data[3][2]=0;
	ans.data[3][3]=1;
	--n;//注意指数是n-1
	while(n>=1)//矩阵快速幂
	{
		if(n%2==1)
		{
			ans=mul(ans,a);
		}
		a=mul(a,a);
		n/=2;
	}
	return ans.data[3][2]+ans.data[3][3];//0*ans.data[3][1]+1*ans.data[3][2]+1*ans.data[3][3]=ans.data[3][2]+ans.data[3][3]
}
int main()
{
	int T;
	scanf("%d",&T);
	while(T--)//注意多组数据
	{
		int x,y;
		scanf("%d%d",&x,&y);//读入x,y
		printf("%d\n",(fsum(y)-fsum(x-1)+mod)%mod);//输出
	}
	return 0;
}
方法2

方法 1 1 1中的矩阵是 3 × 3 3 \times 3 3×3 3 × 1 3 \times 1 3×1的,运算起来常数较大,能不能化简呢?
首先,我们需要知道斐波那契数列的一个性质: ∑ i = 1 n f ( i ) = f ( n + 2 ) − f ( 2 ) \sum_{i=1}^{n}f(i)=f(n+2)-f(2) i=1nf(i)=f(n+2)f(2)。证明如下:

∵ f ( 3 ) = f ( 1 ) + f ( 2 ) , f ( 4 ) = f ( 2 ) + f ( 3 ) , f ( 5 ) = f ( 3 ) + f ( 4 ) , ⋯   , f ( n + 2 ) = f ( n ) + f ( n + 1 ) \because f(3)=f(1)+f(2),f(4)=f(2)+f(3),f(5)=f(3)+f(4),\cdots,f(n+2)=f(n)+f(n+1) f(3)=f(1)+f(2),f(4)=f(2)+f(3),f(5)=f(3)+f(4),,f(n+2)=f(n)+f(n+1)

∴ f ( 1 ) = f ( 3 ) − f ( 2 ) , f ( 2 ) = f ( 4 ) − f ( 3 ) , f ( 3 ) = f ( 5 ) − f ( 4 ) , ⋯   , f ( n ) = f ( n + 2 ) − f ( n + 1 ) \therefore f(1)=f(3)-f(2),f(2)=f(4)-f(3),f(3)=f(5)-f(4),\cdots,f(n)=f(n+2)-f(n+1) f(1)=f(3)f(2),f(2)=f(4)f(3),f(3)=f(5)f(4),,f(n)=f(n+2)f(n+1)

∵ ∑ i = 1 n f ( i ) = f ( 1 ) + f ( 2 ) + f ( 3 ) + ⋯ + f ( n ) \because \sum_{i=1}^{n}f(i)=f(1)+f(2)+f(3)+\cdots+f(n) i=1nf(i)=f(1)+f(2)+f(3)++f(n)

∴ ∑ i = 1 n f ( i ) = f ( 3 ) − f ( 2 ) + f ( 4 ) − f ( 3 ) + f ( 5 ) − f ( 4 ) + ⋯ + f ( n + 2 ) − f ( n + 1 ) \therefore \sum_{i=1}^{n}f(i)=f(3)-f(2)+f(4)-f(3)+f(5)-f(4)+\cdots+f(n+2)-f(n+1) i=1nf(i)=f(3)f(2)+f(4)f(3)+f(5)f(4)++f(n+2)f(n+1)

∑ i = 1 n f ( i ) = f ( n + 2 ) − f ( 2 ) \sum_{i=1}^{n}f(i)=f(n+2)-f(2) i=1nf(i)=f(n+2)f(2)

有了这个性质后,我们求 ∑ i = 1 n f ( i ) \sum_{i=1}^{n}f(i) i=1nf(i)就变成了求 f ( n + 2 ) − f ( 2 ) = f ( n + 2 ) − 1 f(n+2)-f(2)=f(n+2)-1 f(n+2)f(2)=f(n+2)1,。于是我们就可以用可以用矩阵快速幂快速求出斐波那契数列的第 n + 2 n+2 n+2项,迅速得出答案了。
与方法 1 1 1中的推到方法类似,我们可以确定出我们要记录的数值为 f ( n − 1 ) , f ( n ) f(n-1),f(n) f(n1),f(n),且 [ 0 1 1 1 ] × [ f ( n − 1 ) f ( n ) ] = [ f ( n ) f ( n + 1 ) ] \begin{bmatrix} 0 & 1 \\ 1 & 1 \end{bmatrix} \times \begin{bmatrix} f(n-1) \\ f(n) \end{bmatrix} = \begin{bmatrix} f(n) \\ f(n+1) \end{bmatrix} [0111]×[f(n1)f(n)]=[f(n)f(n+1)],令 f ( 0 ) = 0 f(0)=0 f(0)=0,那么
∑ i = 1 n f ( i ) = [ 0 1 1 1 ] n + 2 − 1 × [ f ( 0 ) f ( 1 ) ] 所得最后一项 − 1 = [ 0 1 1 1 ] n + 1 × [ 0 1 ] 所得最后一项 − 1 。 \sum_{i=1}^{n}f(i)= \begin{bmatrix} 0 & 1 \\ 1 & 1 \end{bmatrix} ^{n+2-1} \times \begin{bmatrix} f(0) \\ f(1) \end{bmatrix} \text{所得最后一项} -1=\begin{bmatrix} 0 & 1 \\ 1 & 1 \end{bmatrix} ^{n+1} \times \begin{bmatrix} 0 \\ 1 \end{bmatrix} \text{所得最后一项} -1 \text{。} i=1nf(i)=[0111]n+21×[f(0)f(1)]所得最后一项1=[0111]n+1×[01]所得最后一项1
解题代码如下:

#include<cstdio>
int mod=10000;//模数(记得取模)
struct matrix{int data[3][3];};//矩阵
matrix mul(matrix a,matrix b)//矩阵乘法
{
	matrix ans;
	for(int i=1;i<=2;i++)
	{
		for(int j=1;j<=2;j++)
		{
			ans.data[i][j]=0;
			for(int k=1;k<=2;k++)
			{
				ans.data[i][j]+=a.data[i][k]*b.data[k][j]%mod;
				ans.data[i][j]%=mod;
			}
		}
	}
	return ans;
}
int f(int n)
{
	matrix a/*推出的矩阵*/,ans/*最终矩阵*/;
	a.data[1][1]=0;//初始化a(推出的矩阵)
	a.data[1][2]=1;
	a.data[2][1]=1;
	a.data[2][2]=1;
	ans.data[1][1]=1;//初始化ans(单位矩阵)
	ans.data[1][2]=0;
	ans.data[2][1]=0;
	ans.data[2][2]=1;
	--n;//注意指数
	while(n>=1)//矩阵快速幂
	{
		if(n%2==1)
		{
			ans=mul(ans,a);
		}
		a=mul(a,a);
		n/=2;
	}
	return ans.data[2][2];//0*ans.data[2][1]+1*ans.data[2][2]=ans.data[2][2]
}
int main()
{
	int T;
	scanf("%d",&T);
	while(T--)//注意多组数据
	{
		int x,y;
		scanf("%d%d",&x,&y);//读入x,y
		printf("%d\n",(f(y+2)-f(x+1/*x-1+2=x+1*/)/*(f(y+2)-1)-(f(x+1)-1)=f(y+2)-1-f(x+1)+1=f(y+2)-f(x+1)*/+mod)%mod);//输出
	}
	return 0;
}

总结

有时候一道题不止一种做法,我们要从多方面思考问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值