P1306 斐波那契公约数

Link

一句话题意:

\(f_i\) 表示菲波那切数列第 \(i\) 求项,让你求 \(gcd(f_n,f_m)\)

题解

自己一开始想的是求出 \(f_n,f_m\) 的值,在用辗转相除法求出他们的最大公约数。

开心的打了个矩阵快速幂,结果交上去只有 \(20pts\).

看看了讨论区里面的大佬说 做矩阵快速幂的时候会取模,在取模意义下的公约数是不对的。

然后我的那种做法就被 \(hack\) 了。

正解的推导需要斐波那契数列的一个性质:

  • \(gcd(f_n,f_m) = f_{gcd(n,m)}\)

证明一下:

首先通过反证法可以证出菲波那切的相邻两项是互素的,所以可得 \(gcd(f_n,f_m) = gcd(f_{n-m},f_m)\)

具体证明的话还需要斐波那契数列的一些恒等式,自己不太会证,但记住就行了。

然后你就可以发现这是个辗转相减的形式,到最后可以一定是个这样的形式 \(gcd(0,f_{gcd(n,m)})\) 也就是 \(f_{gcd(n,m)}\)

综上 \(gcd(f_n,f_m) = f_{gcd(n,m)}\)

然后用一下矩阵快速幂求出来第 \(gcd(n,m)\) 项就可以。

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define int long long
const int p = 1e8;
int n,m;
struct node
{
	int a[2][2];
}tmp,x,y;
node operator * (node x,node y)
{
	node c;
	memset(c.a,0,sizeof(c));
	for(int i = 0; i < 2; i++)
	{
		for(int j = 0; j < 2; j++)
		{
			for(int k = 0; k < 2; k++)
			{
				c.a[i][j] = (c.a[i][j] + x.a[i][k] * y.a[k][j] % p) % p;
			}
		}
	}
	return c;
}
node ksm(node a,int b)
{
	node res;
	for(int i = 0; i < 2; i++)
	{
		for(int j = 0; j < 2; j++)
		{
			res.a[i][j] = i == j ? 1 : 0;
		}
	}
	for(; b; b >>= 1)
	{
		if(b & 1) res = res * a;
		a = a * a;
	}
	return res;
}
int gcd(int a,int b)
{
	if(b == 0) return a;
	else return gcd(b,a%b);
}
signed main()
{
	scanf("%lld%lld",&n,&m);
	tmp.a[0][0] = tmp.a[0][1] = tmp.a[1][0] = 1; tmp.a[1][1] = 0;
	x.a[0][0] = 1; x.a[1][0] = 0;
	tmp = ksm(tmp,gcd(n,m)-1);
	x = x * tmp;
	printf("%lld\n",x.a[0][0]);
	return 0;
}
posted @ 2020-09-29 08:46  genshy  阅读(102)  评论(1编辑  收藏  举报