题目大意
求 ∑ 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(i−1)+f(i−2) , (i≥3)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=x∑yf(i)mod10000=[(i=1∑yf(i)mod10000)−(i=1∑x−1f(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
0≤k<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=1∑ymod15000f(i)mod10000)−(i=1∑(x−1)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(n−1),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(n−1)f(n)sumn⎦⎤。那我们要怎么从
[
f
(
n
−
1
)
f
(
n
)
s
u
m
n
]
\begin{bmatrix} f(n-1) \\ f(n) \\ sum_n \end{bmatrix}
⎣⎡f(n−1)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(n−1)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的矩阵:
- 由 f ( n − 1 ) f(n-1) f(n−1)到 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(n−1)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;
- 由 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(n−1)+f(n),而 f ( n − 1 ) f(n-1) f(n−1)出现在 [ f ( n − 1 ) f ( n ) s u m n ] \begin{bmatrix} f(n-1) \\ f(n) \\ sum_n \end{bmatrix} ⎣⎡f(n−1)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(n−1)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;
- 由 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=0∑nf(i)=⎣⎡011111001⎦⎤n−1×⎣⎡f(0)f(1)sum1⎦⎤所得最后一项=⎣⎡011111001⎦⎤n−1×⎣⎡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=1∑nf(i)=i=0∑nf(i)=⎣⎡011111001⎦⎤n−1×⎣⎡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
1≤i≤n,1≤j≤k)
解题的代码如下:
#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(n−1),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(n−1)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=1∑nf(i)=[0111]n+2−1×[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;
}
总结
有时候一道题不止一种做法,我们要从多方面思考问题。