【洛谷 P1306】斐波那契公约数——杨子曰题目
超链接:数学合集
题目描述
对于Fibonacci数列:1,1,2,3,5,8,13…大家应该很熟悉吧~~~但是现在有一个很“简单”问题:第n项和第m项的最大公约数是多少?
输入格式:
两个正整数n和m。(n,m<=10^9)
注意:数据很大
输出格式:
Fn和Fm的最大公约数。
由于看了大数字就头晕,所以只要输出最后的8位数字就可以了。
输入样例:
4 7
输出样例:
1
说明
用递归&递推会超时
用通项公式也会超时
这怕不是一道数学题……
看到题面,我们就可以猜测(别问我是怎么猜到的):
g
c
d
(
f
[
n
]
,
f
[
m
]
)
=
f
[
g
c
d
(
n
,
m
)
]
gcd(f[n],f[m])=f[gcd(n,m)]
gcd(f[n],f[m])=f[gcd(n,m)]
于是我们就来证一证呗!
不过在证这个之前我们先要证明这样一个东东: g c d ( f [ n ] , f [ n + 1 ] ) = 1 gcd(f[n],f[n+1])=1 gcd(f[n],f[n+1])=1也就是斐波那契的相邻两项互质:
欧几里得算法(那是什么?戳)告诉我们:
g
c
d
(
f
[
n
]
,
f
[
n
+
1
]
)
=
g
c
d
(
f
[
n
]
,
f
[
n
+
1
]
−
f
[
n
]
)
gcd(f[n],f[n+1])=gcd(f[n],f[n+1]-f[n])
gcd(f[n],f[n+1])=gcd(f[n],f[n+1]−f[n])
有没有发现
f
[
n
+
1
]
−
f
[
n
]
=
f
[
n
−
1
]
f[n+1]-f[n]=f[n-1]
f[n+1]−f[n]=f[n−1],于是我们得到了:
g
c
d
(
f
[
n
]
,
f
[
n
+
1
]
)
=
g
c
d
(
f
[
n
]
,
f
[
n
−
1
]
)
gcd(f[n],f[n+1])=gcd(f[n],f[n-1])
gcd(f[n],f[n+1])=gcd(f[n],f[n−1])
嗯?这不就说明斐波那契数列所有相邻两项的gcd都相等吗?
显然:gcd(f[1],f[2])=1,这样一来所有的相邻两项gcd就都是1了
得证!
咱们正式开始证明: g c d ( f [ n ] , f [ m ] ) = f [ g c d ( n , m ) ] gcd(f[n],f[m])=f[gcd(n,m)] gcd(f[n],f[m])=f[gcd(n,m)]
首先我们假设
n
<
m
,
f
[
n
]
=
a
,
f
[
n
+
1
]
=
b
n<m,f[n]=a,f[n+1]=b
n<m,f[n]=a,f[n+1]=b
这样一来我们往后推一下发现:
f
[
n
+
1
]
=
a
+
b
,
f
[
n
+
2
]
=
a
+
2
b
,
f
[
n
+
3
]
=
2
a
+
3
b
⋯
⋯
f
[
m
]
=
f
[
m
−
n
−
1
]
a
+
f
[
m
−
n
]
b
f[n+1]=a+b,f[n+2]=a+2b,f[n+3]=2a+3b \cdots \cdots f[m]=f[m−n−1]a+f[m−n]b
f[n+1]=a+b,f[n+2]=a+2b,f[n+3]=2a+3b⋯⋯f[m]=f[m−n−1]a+f[m−n]b
(↑不明白的童鞋自己多模拟几个↑)
然后我们把a和b代掉:
f
[
m
]
=
f
[
m
−
n
−
1
]
∗
f
[
n
]
+
f
[
m
−
n
]
∗
f
[
n
+
1
]
f[m]=f[m−n−1]∗f[n]+f[m−n]∗f[n+1]
f[m]=f[m−n−1]∗f[n]+f[m−n]∗f[n+1]
这样一来:
g
c
d
(
f
[
n
]
,
f
[
m
]
)
=
g
c
d
(
f
[
n
]
,
f
[
m
−
n
−
1
]
∗
f
[
n
]
+
f
[
m
−
n
]
∗
f
[
n
+
1
]
)
gcd(f[n],f[m])=gcd(f[n],f[m−n−1]∗f[n]+f[m−n]∗f[n+1])
gcd(f[n],f[m])=gcd(f[n],f[m−n−1]∗f[n]+f[m−n]∗f[n+1])
而
f
[
m
−
n
−
1
]
∗
f
[
n
]
f[m−n−1]∗f[n]
f[m−n−1]∗f[n]是
f
[
n
]
f[n]
f[n]的倍数,说明他对这个gcd毫无影响,也就是说:
g
c
d
(
f
[
n
]
,
f
[
m
]
)
=
g
c
d
(
f
[
n
]
,
f
[
m
−
n
]
∗
f
[
n
+
1
]
)
gcd(f[n],f[m])=gcd(f[n],f[m−n]∗f[n+1])
gcd(f[n],f[m])=gcd(f[n],f[m−n]∗f[n+1])
上面已经证过了:
g
c
d
(
f
[
n
]
,
f
[
n
+
1
]
)
=
1
gcd(f[n],f[n+1])=1
gcd(f[n],f[n+1])=1,说明
f
[
n
+
1
]
f[n+1]
f[n+1]对这个gcd值毫无任何贡献,我们得到了:
g
c
d
(
f
[
n
]
,
f
[
m
]
)
=
g
c
d
(
f
[
n
]
,
f
[
m
−
n
]
)
gcd(f[n],f[m])=gcd(f[n],f[m−n])
gcd(f[n],f[m])=gcd(f[n],f[m−n])
这个式子就很神奇了,有没有发现它有点像欧几里得算法(戳我),我们可以得到的是:
g
c
d
(
f
[
n
]
,
f
[
m
]
)
=
g
c
d
(
f
[
n
]
,
f
[
m
m
o
d
n
]
)
gcd(f[n],f[m])=gcd(f[n],f[m\ mod\ n])
gcd(f[n],f[m])=gcd(f[n],f[m mod n])
简单解释一下这一步是怎么推过来的,对于上面的上面的那个式子,如果我们把
f
[
n
−
m
]
f[n-m]
f[n−m]这看成等式左边一项新的
f
[
m
]
f[m]
f[m]就可以继续把下标减n,一直减一直减,直到不能减了为止,就是
m
m
o
d
n
m\ mod \ n
m mod n(和欧几里得算法的想法是一样滴)
根据这个式子: g c d ( f [ n ] , f [ m ] ) = g c d ( f [ n ] , f [ m m o d n ] ) gcd(f[n],f[m])=gcd(f[n],f[m\ mod\ n]) gcd(f[n],f[m])=gcd(f[n],f[m mod n])我们就可以像欧几里得算法一样不停的的递归下去了
直到m变成了0,答案就是f[n],有没有发现这时的n就是最开始的gcd(m,n)
得证
题目已经告诉你了不要用递归,递推,通项公式之类的东西
So,我们不得不 使用矩阵快速幂(你也可以使用一个更加神奇的方法,在O(log n)的时间里求出斐波那契第n项,戳我,这篇文章里就不多讲了)
最终想法:用矩阵快速幂求出f[gcd(n,m)] mod 100000000,即为答案
复杂度:O(log n)
OK,完事
c++代码:
#include<bits/stdc++.h>
using namespace std;
int p=100000000;
struct Matrix{
int a[3][3];
void init(){
memset(a,0,sizeof(a));
}
void one(){
memset(a,0,sizeof(a));
for (int i=1;i<=2;i++){
a[i][i]=1;
}
}
}f;
Matrix operator * (Matrix x,Matrix y){
Matrix c;
c.init();
for (int i=1;i<=2;i++){
for (int j=1;j<=2;j++){
for (int k=1;k<=2;k++){
c.a[i][j]=(c.a[i][j]+1LL*x.a[i][k]*y.a[k][j])%p;
}
}
}
return c;
}
Matrix operator ^ (Matrix x,int k){
Matrix ans;
ans.one();
while(k){
if (k&1) ans=ans*x;
x=x*x;
k>>=1;
}
return ans;
}
int gcd(int a,int b){
return b==0?a:gcd(b,a%b);
}
int fib(int n){
if (n==0) return 0;
if (n==1) return 1;
f=f^(n-2);
return (f.a[1][2]+f.a[2][2])%p;
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
f.a[1][1]=0;
f.a[1][2]=1;
f.a[2][1]=1;
f.a[2][2]=1;
cout<<fib(gcd(n,m));
return 0;
}
参考:https://www.luogu.org/blog/five20/solution-p1306
于HG机房
本文深入探讨洛谷P1306斐波那契公约数问题,解析了如何利用数学性质证明斐波那契数列中任意两项的最大公约数等于其下标最大公约数对应的斐波那契数,最终采用矩阵快速幂算法高效求解。


被折叠的 条评论
为什么被折叠?



