洛谷 P1306 斐波那契公约数 题解

CSDN同步

原题链接

前置知识:

矩阵乘法、斐波那契数列的矩阵公式。

题意简要:

\(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;
}

posted @ 2020-03-15 17:46  bifanwen  阅读(209)  评论(0)    收藏  举报