P1306 斐波那契公约数
一句话题意:
\(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;
}