超链接:数论合集
就是说让你在O(log n)的时间里告诉你斐波那契数列第n项是谁
然而,我们不使用矩阵快速幂(这种难理解的东西 )
我们要使用一种更加高级,更好理解的东西——斐波那契数列二倍项公式
咱们先上公式:
f 2 n = f n ∗ ( f n − 1 + f n + 1 ) f_{2n}=f_n*(f_{n-1}+f_{n+1}) f2n=fn∗(fn−1+fn+1)
然后来一个nb的证明:
众所周知,斐波那契数列的第n项是可以表示:我爬楼梯,开始时站在第1级台阶上,可以一步跨一级,或者一步跨两级的,爬到第n级的爬法总数
上面这个很好理解,想要爬到第n级,我们可以从第n-2级爬两级上来,也可以从n-1级跨一级上来,那么通过加法原理我们得到了: f n = f n − 2 + f n − 1 f_n=f_{n-2}+f_{n-1} fn=fn−2+fn−1,也就是斐波那契数列
知道了这一点以后,我们回归正题,现在我们要求 f 2 n f_{2n} f2n,也就是我们要爬到第2n级台阶,现在我们把第n级台阶视为一个中转站来考虑这个问题
显然上面两种情况是并列的,So,我们使用加法原理,得到最终答案:
f 2 n = f n − 1 ∗ f n + f n ∗ f n + 1 f_{2n}=f_{n-1}*f_n+f_n*f_{n+1} f2n=fn−1∗fn+fn∗fn+1
f 2 n = f n ∗ ( f n − 1 + f n + 1 ) f_{2n}=f_n*(f_{n-1}+f_{n+1}) f2n=fn∗(fn−1+fn+1)
得证
BUT,你有没有发现这个东西有个BUG,它只能算偶数项,不能算奇数项,所以我们还要来推一推:
首先,我们从 f 2 n + 2 = f 2 n + 1 + f 2 n f_{2n+2}=f_{2n+1}+f_{2n} f2n+2=f2n+1+f2n开始,移个项,我们得到了:
f 2 n + 1 = f 2 n + 2 − f 2 n f_{2n+1}=f_{2n+2}-f_{2n} f2n+1=f2n+2−f2n
哦,有没有发现右边两个都是偶数项,那么用我们上面推出的公式代掉,得到了:
f 2 n + 1 = f n + 1 ∗ ( f n + f n + 2 ) − f n ∗ ( f n − 1 + f n + 1 ) f_{2n+1}=f_{n+1}*(f_n+f_{n+2})-f_n*(f_{n-1}+f_{n+1}) f2n+1=fn+1∗(fn+fn+2)−fn∗(fn−1+fn+1)
虽然右边的下标已经没有系数2了,但是由于太丑了,我们继续往下化简,把括号拆开:
f 2 n + 1 = f n + 1 ∗ f n + f n + 1 ∗ f n + 2 − f n ∗ f n − 1 − f n ∗ f n + 1 f_{2n+1}=f_{n+1}*f_n+f_{n+1}*f_{n+2}-f_n*f_{n-1}-f_n*f_{n+1} f2n+1=fn+1∗fn+fn+1∗fn+2−fn∗fn−1−fn∗fn+1
有没有发现第一项和第四项可以抵消掉:
f 2 n + 1 = f n + 1 ∗ f n + 2 − f n ∗ f n − 1 f_{2n+1}=f_{n+1}*f_{n+2}-f_n*f_{n-1} f2n+1=fn+1∗fn+2−fn∗fn−1
这个式子还是有一个缺点算一个 f 2 n + 1 f_{2n+1} f2n+1得先算出四项,会拉低程序的效率,So,我们继续化简:
f 2 n + 1 = f n + 1 ∗ ( f n + f n + 1 ) − f n ∗ ( f n + 1 − f n ) f_{2n+1}=f_{n+1}*(f_n+f_{n+1})-f_n*(f_{n+1}-f_n) f2n+1=fn+1∗(fn+fn+1)−fn∗(fn+1−fn)
f 2 n + 1 = f n + 1 ∗ f n + ( f n + 1 ) 2 − f n ∗ f n + 1 + f n 2 f_{2n+1}=f_{n+1}*f_n+(f_{n+1})^2-f_n*f_{n+1}+{f_n}^2 f2n+1=fn+1∗fn+(fn+1)2−fn∗fn+1+fn2
这样一来:
f 2 n + 1 = ( f n + 1 ) 2 + f n 2 f_{2n+1}=(f_{n+1})^2+{f_n}^2 f2n+1=(fn+1)2+fn2
多么优美的公式啊!
再给大家一个相对理性一点的证明(从我这篇文章发现的):
首先我们假设 n < m , f [ n ] = a , f [ n + 1 ] = b n<m,f[n]=a,f[n+1]=b n<m,f[n]=a,f[n+1]=b
这样一来我们往后推一下发现:
f [ n + 1 ] = a + b , f [ n + 2 ] = a + 2 b , f [ n + 3 ] = 2 a + 3 b ⋯ ⋯ f [ m ] = f [ m − n − 1 ] a + f [ m − n ] b f[n+1]=a+b,f[n+2]=a+2b,f[n+3]=2a+3b \cdots \cdots f[m]=f[m−n−1]a+f[m−n]b f[n+1]=a+b,f[n+2]=a+2b,f[n+3]=2a+3b⋯⋯f[m]=f[m−n−1]a+f[m−n]b
然后我们把m分别用2n和2n+1代掉也同样可以得到上面的结论
然后我们就可以再O(log n)的时间了递归求出 f n f_n fn了,别忘了要把当前求过的值记录一下,就有点类似记忆化搜索,可以使用map(好东西)
OK,完事
c++代码(洛谷P1962):
#include
#define ll long long
#define p 1000000007
using namespace std;
ll n;
map<ll,ll> mp;
ll calc(ll n){
if (mp[n]) return mp[n];
ll tmp=n;n/=2;
if (tmp%2) return mp[tmp]=calc(n)*calc(n)%p+calc(n+1)*calc(n+1)%p;
else return mp[tmp]=(2*calc(n-1)%p+calc(n)%p)*calc(n)%p;
}
int main(){
scanf("%lld",&n);
mp[1]=1;
mp[2]=1;
cout<<calc(n)%p;
return 0;
}
参考:
https://www.zhihu.com/question/49642985/answer/329341469?edition=yidianzixun&utm_source=yidianzixun&yidian_docid=K_006PAjUf&yidian_s=9&yidian_appid=
https://chhokmah.blog.luogu.org/solution-p1962
于HG机房