矩阵快速幂 - 佳佳的斐波那契 - AcWing 1304
佳佳对数学,尤其对数列十分感兴趣。
在研究完 Fibonacci 数列后,他创造出许多稀奇古怪的数列。
例如用 S(n) 表示 Fibonacci 前 n 项和 mod m 的值,
即 S ( n ) = ( F 1 + F 2 + … + F n ) m o d m , 其 中 F 1 = F 2 = 1 , F i = F i − 1 + F i − 2 。 即 S(n)=(F_1+F_2+…+F_n)mod\ m,其中 F_1=F_2=1,F_i=F_{i−1}+F_{i−2}。 即S(n)=(F1+F2+…+Fn)mod m,其中F1=F2=1,Fi=Fi−1+Fi−2。
可这对佳佳来说还是小菜一碟。
终于,她找到了一个自己解决不了的问题。
用 T ( n ) = ( F 1 + 2 F 2 + 3 F 3 + … + n F n ) m o d m 表 示 F i b o n a c c i 数 列 前 n 项 变 形 后 的 和 m o d m 的 值 。 用 T(n)=(F_1+2F_2+3F_3+…+nF_n)mod\ m 表示 Fibonacci 数列前 n 项变形后的和 mod\ m 的值。 用T(n)=(F1+2F2+3F3+…+nFn)mod m表示Fibonacci数列前n项变形后的和mod m的值。
现在佳佳告诉你了一个 n 和 m,请求出 T(n) 的值。
输入格式
共一行,包含两个整数 n 和 m。
输出格式
共一行,输出 T(n) 的值。
数据范围
1 ≤ n , m ≤ 2 31 − 1 1≤n,m≤2^{31}−1 1≤n,m≤231−1
输入样例:
5 5
输出样例:
1
样例解释
T ( 5 ) = ( 1 + 2 × 1 + 3 × 2 + 4 × 3 + 5 × 5 ) m o d 5 = 1 T(5)=(1+2×1+3×2+4×3+5×5)mod\ 5=1 T(5)=(1+2×1+3×2+4×3+5×5)mod 5=1
分析:
求 S n : 求S_n: 求Sn:矩阵快速幂 - 斐波那契前 n 项和 - AcWing 1303
已 知 S n 与 F n 的 关 系 , 现 要 将 T n 与 S n 和 F n 建 立 关 系 , 通 过 已 知 来 求 未 知 , 已知S_n与F_n的关系,现要将T_n与S_n和F_n建立关系,通过已知来求未知, 已知Sn与Fn的关系,现要将Tn与Sn和Fn建立关系,通过已知来求未知,
观 察 发 现 , T n 相 比 于 S n , 关 于 n 的 次 数 要 高 一 次 , 观察发现,T_n相比于S_n,关于n的次数要高一次, 观察发现,Tn相比于Sn,关于n的次数要高一次,
我 们 先 给 S n 升 幂 , 乘 一 个 n , 我们先给S_n升幂,乘一个n, 我们先给Sn升幂,乘一个n,
{ T n = F 1 + 2 F 2 + 3 F 3 + … + n F n n S ( n ) = n F 1 + n F 2 + n F 3 + … + n F n \begin{cases}T_n=F_1+2F_2+3F_3+…+nF_n\\ \\nS(n)=nF_1+nF_2+nF_3+…+nF_n\end{cases} ⎩⎪⎨⎪⎧Tn=F1+2F2+3F3+…+nFnnS(n)=nF1+nF2+nF3+…+nFn
记 P n = n S n − T n = ( n − 1 ) F 1 + ( n − 2 ) F 2 + ( n − 3 ) F 3 + . . . + F n − 1 ① 记P_n=nS_n-T_n=(n-1)F_1+(n-2)F_2+(n-3)F_3+...+F_{n-1}\qquad\qquad\ ① 记Pn=nSn−Tn=(n−1)F1+(n−2)F2+(n−3)F3+...+Fn−1 ①
则 P n + 1 = ( n + 1 ) S n + 1 − T n + 1 = n F 1 + ( n − 1 ) F 2 + ( n − 2 ) F 3 + . . . + F n ② 则P_{n+1}= (n+1)S_{n+1}-T_{n+1}=nF_1+(n-1)F_2+(n-2)F_3+...+F_n\quad\quad\ \ ② 则Pn+1=(n+1)Sn+1−Tn+1=nF1+(n−1)F2+(n−2)F3+...+Fn ②
② − ① 得 P n + 1 − P n = F 1 + F 2 + . . . + F n = S n , 即 P n + 1 = P n + S n ②-①得P_{n+1}-P_n=F_1+F_2+...+F_n=S_n,即P_{n+1}=P_n+S_n ②−①得Pn+1−Pn=F1+F2+...+Fn=Sn,即Pn+1=Pn+Sn
到 这 里 , 我 们 就 将 S n 、 P n 、 F n 之 间 建 立 了 递 推 关 系 , 求 出 P n 后 , 可 以 解 得 : T n = n S n − P n 。 到这里,我们就将S_n、P_n、F_n之间建立了递推关系,求出P_n后,可以解得:T_n=nS_n-P_n。 到这里,我们就将Sn、Pn、Fn之间建立了递推关系,求出Pn后,可以解得:Tn=nSn−Pn。
设 矩 阵 Q n = [ F n F n + 1 S n P n ] , 现 需 凑 出 4 × 4 变 换 矩 阵 A , 使 得 Q n A = Q n + 1 设矩阵Q_n=[F_n\quad F_{n+1}\quad S_n\quad P_n],现需凑出4×4变换矩阵A,使得Q_nA=Q_{n+1} 设矩阵Qn=[FnFn+1SnPn],现需凑出4×4变换矩阵A,使得QnA=Qn+1
F n + 1 = 0 × F n + 1 × F n + 1 + 0 × S n + 0 × P n F_{n+1}=0×F_n+1×F_{n+1}+0×S_n+0×P_n Fn+1=0×Fn+1×Fn+1+0×Sn+0×Pn
F n + 2 = 1 × F n + 1 × F n + 1 + 0 × S n + 0 × P n F_{n+2}=1×F_n+1×F_{n+1}+0×S_n+0×P_n Fn+2=1×Fn+1×Fn+1+0×Sn+0×Pn
S n + 1 = 0 × F n + 1 × F n + 1 + 1 × S n + 0 × P n S_{n+1}=0×F_n+1×F_{n+1}+1×S_n+0×P_n Sn+1=0×Fn+1×Fn+1+1×Sn+0×Pn
P n + 1 = 0 × F n + 0 × F n + 1 + 1 × S n + 1 × P n P_{n+1}=0×F_n+0×F_{n+1}+1×S_n+1×P_n Pn+1=0×Fn+0×Fn+1+1×Sn+1×Pn
于 是 变 换 矩 阵 A = ∣ 0 1 0 0 1 1 1 0 0 0 1 1 0 0 0 1 ∣ , 初 始 矩 阵 Q 1 = [ F 1 F 2 S 1 P 1 ] = [ 1 1 1 0 ] 于是变换矩阵A=\begin{vmatrix}0\quad1\quad0\quad0\\1\quad1\quad1\quad0\\0\quad0\quad1\quad1\\0\quad0\quad0\quad1\end{vmatrix},初始矩阵Q_1=[F_1\quad F_{2}\quad S_1\quad P_1]=[1\quad 1\quad 1\quad 0] 于是变换矩阵A=∣∣∣∣∣∣∣∣0100111000110001∣∣∣∣∣∣∣∣,初始矩阵Q1=[F1F2S1P1]=[1110]
先 计 算 A n − 1 , 再 计 算 Q n = Q 1 A n − 1 = [ F n F n + 1 S n P n ] 先计算A^{n-1},再计算Q_n=Q_1A^{n-1}=[F_n\quad F_{n+1}\quad S_n\quad P_n] 先计算An−1,再计算Qn=Q1An−1=[FnFn+1SnPn]
最 后 得 到 T n = n S n − P n , 注 意 负 数 取 模 。 最后得到T_n=nS_n-P_n,注意负数取模。 最后得到Tn=nSn−Pn,注意负数取模。
代码:
#include<iostream>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=4;
int n,m;
ll f1[N][N]={{1,1,1,0}}; //Pn=n*Sn-Tn
ll A[N][N]={
{0,1,0,0},
{1,1,1,0},
{0,0,1,1},
{0,0,0,1}
};
void mul(ll a[][N],ll b[][N])
{
ll c[N][N]={0};
for(int i=0;i<N;i++)
for(int j=0;j<N;j++)
for(int k=0;k<N;k++)
c[i][j]=(c[i][j]+a[i][k]*b[k][j]%m)%m;
memcpy(a,c,sizeof c);
}
void quick_pow(ll a[][N],int k)
{
ll E[N][N]={
{1,0,0,0},
{0,1,0,0},
{0,0,1,0},
{0,0,0,1}
};
while(k)
{
if(k&1) mul(E,a);
mul(a,a);
k>>=1;
}
memcpy(a,E,sizeof E);
}
int main()
{
cin>>n>>m;
quick_pow(A,n-1);
mul(f1,A);
ll ans=(n*f1[0][2]%m-f1[0][3]+m)%m;
cout<<ans<<endl;
return 0;
}