假期第一天,当然是好好学习啦。今天去牛客网上一玩,果不其然的。。自闭了。。算了,自闭了也要有所收获吧。分享一道数论题,有点意思,涉及到很多东西,弄懂的话能学到很多。
题目描述
链接:https://ac.nowcoder.com/acm/problem/200091
大概意思:f表示斐波那契数列(定义f0=0,f1=1,f2=1…)。输入两个数m,n,求出fm,fn的最大公因数。
思路分析
看到题目,第一眼应该感觉很简单。动笔之前,看了一眼数据范围:1 ≤m,n ≤ 2^31。。。我人傻了??这样只是暴力的话肯定是做不出来的。
于是,思考了良久,并在大佬的帮助下,找到了几个数学方面的知识点:
1、斐波那契数和项数之间的关系:gcd(f(m),f(n))==f(gcd(m,nn));
大佬博客:https://blog.csdn.net/alan_cty/article/details/73928751
2、斐波那契数列和矩阵之间的关系:
大佬博客:https://blog.csdn.net/qq_41286356/article/details/89437065
有了这两个工具,相信思路就不复杂了:首先,利用第一个数学关系,得出所要求的斐波那契数的项数(这里提示下,stl含有__gcd()函数,直接求出最大公约数),之后,再用快速幂得出斐波那契数。在此之前,可以定义一个表示矩阵的结构体,然后重载一下*运算符,这样后续操作或许能简单一些。(当然,直接定函数或者其他手法也行,总体思路一样就好)。通过这道题确实收获良多,希望对各位有所帮助。
完整代码
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef unsigned long long ll;
const int mod=1e8;
struct mat{
ll a[15][15];
mat(){
memset(a,0,sizeof(a));
}
mat operator*(mat b){
mat ans;
for(int i=0;i<2;i++)
{
for(int j=0;j<2;j++)
{
for(int k=0;k<2;k++)
{
ans.a[i][j]+=(a[i][k]*b.a[k][j]);
}
}
}
return ans;
}
};
ll fun(ll i)
{
mat bas;
mat res;
for(int j=0;j<2;j++) res.a[j][j]=1;
bas.a[0][0]=bas.a[0][1]=bas.a[1][0]=1;
bas.a[1][1]=0;
while(i--) res=res*bas;
return res.a[0][1];
}
int main()
{
ll n,m;
int t;
cin>>t;
while(t--)
{
scanf("%lld %lld",&n,&m);
if(n<m) swap(n,m);
printf("%lld\n",fun(__gcd(n,m)));
}
return 0;
}
最后,祝大家假期愉快~