洛谷 P1306 斐波那契公约数 题解
前置知识:
矩阵乘法、斐波那契数列的矩阵公式。
题意简要:
用 \(F_i\) 表示斐波那契数列的第 \(i\) 项,求:
\[\gcd(F_n,F_m)
\]
我们先考虑 \(n > m\) .
首先我们考虑 \(\gcd\) 的两个性质,后面要用:
\[\gcd(a,b) = \gcd(b,a-b) = \gcd(a-k \cdot b)
\]
\[\gcd(a \times b,c) = \gcd(a ,c ) \times \gcd(b,c)
\]
再考虑 \(F\) 数列的一个性质:
\[F_{n+1} - F_n = F_{n-1}
\]
下面先求另一个东西:
\[\gcd(F_n,F_{n+1}) = \gcd(F_n,F_{n+1}-F_n) = \gcd(F_{n-1},F_n) = \cdots = \gcd(F_1,F_2)=1
\]
得出结论:相邻两个 \(F\) 的数,其 \(\gcd\) 为 \(1\).
可以推论:
\[F_{n+m}=F_{n+m-1}+F_{n+m-2} = 2 \times F_{n+m-2} + F_{n+m-3} = 3\times F_{n+m-3} +2 \times F_{n+m-4} = \cdots = F_{n+1} \cdot F_m + F_n \cdot F_{m-1}
\]
如果还不懂,可以从上面这个式子进行推论:
\[F_{n+m}=F_2 \cdot F_{n+m-1}+F_1 \cdot F_{n+m-2} = F_3 \cdot \times F_{n+m-2} + F_2 \cdot F_{n+m-3} = F_4 \cdot F_{n+m-3} +F_3 \cdot F_{n+m-4} = \cdots = F_{n+1} \cdot F_m + F_n \cdot F_{m-1}
\]
(因为每次你会发现,\(F\) 的系数就是 \(F\)本身!)
下面可以得出:
\[\gcd(F_{n+m},F_n) = \gcd(F_{n+1} \cdot F_m + F_n \cdot F_{m-1} ,F_n)
\]
下面由于 \(\gcd\) 的性质,约去 \(F_n\) 一项,再拆开做乘法,可得:
\[= \gcd(F_{n+1} \cdot F_m , F_n) = \gcd(F_{n+1},F_n) \times \gcd(F_m,F_n)
\]
由上面所说结论 \(\gcd(F_{n+1},F_n=1)\) 可推断:
\[\gcd(F_{n+m},F_n) = \gcd(F_m,F_n)
\]
由此不断迭代:
\[\gcd(F_{n+m},F_n) = \gcd(F_m,F_n) = \gcd(F_{n-m},F_m) = \cdots
\]
你会发现,这其实就是在实施 辗转相减法 。
那么,辗转相减法的本质就是在求 \(\gcd\) !
所以可以得出:
\[\gcd(F_n,F_m) = F_{\gcd(n,m)}
\]
由此,问题简化为:
求 \(F\) 数列的第 \(\gcd(n,m)\) 项。
显然(如果不会矩阵乘法,或者是矩阵加速等的同学,可以先去学一下),用 矩阵快速幂 可以优雅地解决本题。
时间复杂度为: \(O(\log n)\). 空间复杂度为: \(O(1)\).(常数级的矩阵大小不计)
(这是因为 \(\gcd(n,m)\) 最大为 \(n\) 当且仅当 \(n=m\))
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MOD=1e8;
inline ll read(){char ch=getchar();int f=1;while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
ll x=0;while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;}
ll n,k;
struct martix {
ll a[3][3];
}; //因为函数不能返回数组,只好写结构体
martix ans;
martix a,b;
inline ll gcd(ll n,ll m) {return m?gcd(m,n%m):n;} //gcd迭代
inline martix chengfa(martix x,martix y) {
martix ans;
for(int i=1;i<=2;i++) for(int j=1;j<=2;j++) ans.a[i][j]=0;
for(int i=1;i<=2;i++) for(int j=1;j<=2;j++) for(int k=1;k<=2;k++) {
ans.a[i][j]+=x.a[i][k]*y.a[k][j];
ans.a[i][j]%=MOD;
} return ans;
} //矩阵乘法
inline martix pw(martix a,ll k) {
for(int i=1;i<=2;i++) ans.a[i][i]=1;
while(k>0) {
if(k&1) ans=chengfa(ans,a);
a=chengfa(a,a); k>>=1;
} return ans;
} //矩阵快速幂
int main(){
n=read(),k=read();
if(n<=2) {printf("1");return 0;}
a.a[1][1]=1; a.a[1][2]=1;
b.a[1][1]=0; b.a[1][2]=1; b.a[2][1]=1; b.a[2][2]=1;
martix t=pw(b,gcd(n,k)-2);
ans=chengfa(a,t);
printf("%lld\n",ans.a[1][2]); //得出最终结果
return 0;
}
简易的代码胜过复杂的说教。

浙公网安备 33010602011771号