佳佳的斐波那契
题目描述
核心思路
由题目可知, S n = f 1 + f 2 + ⋯ + f n S_n=f_1+f_2+\cdots +f_n Sn=f1+f2+⋯+fn, T n = f 1 + 2 f 2 + ⋯ + ( n − 1 ) f n − 1 + n f n T_n=f_1+2f_2+\cdots +(n-1)f_{n-1}+nf_n Tn=f1+2f2+⋯+(n−1)fn−1+nfn
我们设 p n = n S n − T n p_n=nS_n-T_n pn=nSn−Tn,则会有 p n = ( n − 1 ) f 1 + ( n − 2 ) f 2 + ⋯ + 1 f n − 1 + 0 f n p_n=(n-1)f_1+(n-2)f_2+\cdots +1f_{n-1}+0f_n pn=(n−1)f1+(n−2)f2+⋯+1fn−1+0fn,记为【1】式
我们设 p n + 1 = ( n + 1 ) S n + 1 − T n + 1 = n f 1 + ( n − 1 ) f 2 + ⋯ + 2 f n − 1 + 1 f n p_{n+1}=(n+1)S_{n+1}-T_{n+1}=nf_1+(n-1)f_2+\cdots +2f_{n-1}+1f_n pn+1=(n+1)Sn+1−Tn+1=nf1+(n−1)f2+⋯+2fn−1+1fn,记为【2】式
用【2】-【1】,可以得到:
p n + 1 − p n = f 1 + f 2 + ⋯ + f n − 1 + f n = S n p_{n+1}-p_n=f_1+f_2+\cdots +f_{n-1}+f_n=S_n pn+1−pn=f1+f2+⋯+fn−1+fn=Sn,也就是有 p n + 1 − p n = S n p_{n+1}-p_n=S_n pn+1−pn=Sn
由于 p n = n S n − T n p_n=nS_n-T_n pn=nSn−Tn,因此如果我们想要求出 T n T_n Tn,则需要先求出 p n p_n pn和 S n S_n Sn。
我们设 F n F_n Fn= [ f n f n + 1 S n p n ] \left [\begin {matrix} f_n & f_{n+1} & S_n & p_n\end{matrix} \right] [fnfn+1Snpn],设 F n + 1 = [ f n + 1 f n + 2 S n + 1 p n + 1 ] F_{n+1}=\left [\begin {matrix} f_{n+1} & f_{n+2} & S_{n+1} & p_{n+1}\end{matrix} \right] Fn+1=[fn+1fn+2Sn+1pn+1],
构造一个矩阵 A = [ 0 1 0 0 1 1 1 0 0 0 1 1 0 0 0 1 ] A=\left [\begin {matrix} 0 & 1 & 0 &0\\ 1& 1& 1& 0\\ 0&0&1&1\\ 0&0&0&1\end{matrix} \right] A=⎣⎢⎢⎡0100110001100011⎦⎥⎥⎤,那么就会有 F n × A = F n + 1 F_n\times A=F_{n+1} Fn×A=Fn+1。
通过递推可知, F n = F 1 × A n − 1 F_n=F_1\times A^{n-1} Fn=F1×An−1,其中 F 1 F_1 F1是边界, F 1 F_1 F1= [ f 1 f 2 S 1 p 1 ] \left [\begin {matrix} f_1 & f_{2} & S_1 & p_1\end{matrix} \right] [f1f2S1p1], f 1 = 1 , f 2 = 1 f_1=1,f_2=1 f1=1,f2=1,而 S 1 = f 1 = 1 S_1=f_1=1 S1=f1=1,由于 p n = n S n − T n p_n=nS_n-T_n pn=nSn−Tn,那么 p 1 = S 1 − T 1 p_1=S_1-T_1 p1=S1−T1,而 T 1 = f 1 = 1 T_1=f_1=1 T1=f1=1,所以 p 1 = 1 − 1 = 0 p_1=1-1=0 p1=1−1=0
所以,我们只需要用矩阵快速幂求出 A n − 1 A^{n-1} An−1就好了。然后 T n = n S n − p n T_n=nS_n-p_n Tn=nSn−pn,即可求得结果 T n T_n Tn。
代码
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=4;
typedef long long LL;
int n,m;
void mul(int a[][N],int b[][N],int c[][N])
{
int tmp[N][N]={0};
for(int i=0;i<N;i++)
{
for(int j=0;j<N;j++)
{
for(int k=0;k<N;k++)
{
tmp[i][j]=(tmp[i][j]+(LL)a[i][k]*b[k][j])%m;
}
}
}
memcpy(c,tmp,sizeof tmp);
}
int main()
{
scanf("%d%d",&n,&m);
//本来边界F1={1,1,1,0},但是我们这里为了只写一个mul函数,所以把它改造成了N*N的矩阵
//这样就相当于只处理了两个矩阵相乘,方便了计算
int F[N][N]={
{1,1,1,0},
{0,0,0,0},
{0,0,0,0},
{0,0,0,0}
};
//构造矩阵A
int a[N][N]={
{0,1,0,0},
{1,1,1,0},
{0,0,1,1},
{0,0,0,1}
};
int b=n-1;
while(b)
{
if(b&1)
mul(F,a,F); //相当于res=res*a
mul(a,a,a); //相当于a=a*a
b>>=1;
}
//注意这里有可能两个数相减结果会为负数,如果取模得到的结果就是负的
//为了得到正的模数,可以采用(x%m+m)%m的方法
printf("%d\n",(((LL)n * F[0][2] - F[0][3]) % m + m) % m);
}