前言:好像没啥好写的,链接可能还没有更新完
快速幂
快速幂,用于解决当n很大时的情况。通常与取模同时应用。
用最笨的方法求,即。时间复杂度为,而快速幂(附带取模),可以将时间复杂度降低为。
利用倍增的思想,例如,等于,又等于,即,如果n为奇数,,那么换成代码就是:
ll Powermod(ll a,ll b)
{
ll ans=1;
a=a%mod;
while(b>0)
{
if(b%2==1) ans=(ans*a)%mod;
b=b/2;
a=(a*a)%mod;
}
return ans;
}
其中的取模操作保证对最终答案取模结果不变,且防止超出数据类型范围。
矩阵快速幂
前置知识:矩阵乘法
设A为 的矩阵,B为 的矩阵,那么称的矩阵C为矩阵A与B的乘积,记作
,其中矩阵C中的第行第列元素可以表示为:
如下所示:
(来源:搜狗百科)
矩阵乘法时间复杂度:
一个的矩阵和一个的矩阵相乘,将会得到一个的矩阵,
对于矩阵,,表示n行m列的矩阵,的时间复杂度是,代码对应如下:
//矩阵乘法
void MXMP(int m,int n,int p)//Matrix multiplication
{
for(int i = 1;i<=m;i++)
for(int j = 1;j<=p;j++)
for(int k = 1;k<=m;k++)
C[i][j] = (C[i][j] + (A[i][k]*B[k][j])%mod)%mod;//防止超界
}
矩阵快速幂
矩阵快速幂,即给定一个矩阵),快速计算。一般来说,矩阵快速幂只会涉及方阵即,所以这里以方阵为例。
自己写一个矩阵乘法,然后用快速幂的套进去就是矩阵快速幂。
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<queue>
#include<vector>
#include<stack>
using namespace std;
typedef long long int ll;
const int mod = (int)1e9+7;
const int MAX_N = 1e3;
int A[MAX_N][MAX_N],res[MAX_N][MAX_N];
int temp[MAX_N][MAX_N];
//A = A*B
void MXMP(int a[][MAX_N],int b[][MAX_N],int n)
{
//重置临时矩阵temp
for(int i = 1;i<=n;i++)
for(int j = 1;j<=n;j++)
temp[i][j] = 0;
for(int i = 1;i<=n;i++)
for(int j = 1;j<=n;j++)
for(int k = 1;k<=n;k++)
temp[i][j] = (temp[i][j] + (a[i][k]*b[k][j])%mod)%mod;//防止超界
for(int i = 1;i<=n;i++)
for(int j = 1;j<=n;j++)
a[i][j] = temp[i][j];
}
//A = A**x
void PowerMod(int A[][MAX_N],int n,int x)//x为次幂,n为矩阵行/列
{
memset(res,0,sizeof(res));
for(int i = 1;i<=n;i++) res[i][i] = 1;//初始化为单位矩阵
while(x){
if(x&1) MXMP(res,A,n);
MXMP(A,A,n);
x >>= 1;
}
for(int i = 1;i<=n;i++)
for(int j = 1;j<=n;j++)
A[i][j] = res[i][j];
}
int main()
{
//读入A矩阵
int n,x;
cin>>n>>x;//n行n列,x为次幂
for(int i = 1;i<=n;i++)
for(int j = 1;j<=n;j++)
cin>>A[i][j];
//_______________________________
PowerMod(A,n,x);
//检查A矩阵
for(int i = 1;i<=n;i++){
for(int j = 1;j<=n;j++){
cout<<A[i][j]<<" ";
}
cout<<endl;
}
//________________________________
return 0;
}
矩阵快速幂的应用
矩阵快速幂本身并不难,难点在于,怎么把一个递推问题转化成矩阵的形式,为什么矩阵快速幂通常只会涉及方阵?通过应用我们才看得出来。
利用矩阵快速幂求斐波那契数列
斐波那契数列,,换成矩阵乘法的形式,即:
再次递推,
那么就可以得到:
可以手算一下感受一下我们为什么要这样构造矩阵。其实就是递推+乘法结合律。
斐波拉契数列的问题可以拿这道题练手: Problem - 2793 (hdu.edu.cn)
相关题解自己网上搜啊,相对来说算是板子题,题解就不写了。
熟悉了第一种后,接下来是第二种题型:
计算k次方和--二项式展开
在学习了这种题型后,你就会发现,快速幂矩阵简直妙不可言。太爽了。
这里先贴一个链接,这个博主讲的十分透彻清晰,咱也是在这篇文章里学的。以下有些公式会从这篇博客取过来,如有侵权,立删。 根据递推公式构造系数矩阵用于快速幂_RBS的专栏-CSDN博客
这类题型主要涉及这样的公式:
如果不太理解这个式子,这里有一道典型题的链接:
Recursive sequence - HDU 5950 - Virtual Judge (vjudge.net)
建议先去看题,尝试着构造构造,构造不出来再来看效果更好。
现在我们回到上面那个公式,易得,关键在于这个,如果用构造斐波拉契数列的思路构造矩阵,你会发现构造不出来,因为底数本身在不断变化,简单矩阵相乘肯定是不能成功状态转移的。
所以根本问题是解决这个:将再加1。即,将看为一个整体,就变成了二项式,然后再二项式展开:
令
神奇的地方是,我们对原来的进行状态转移就等于现在将每一项的变为,这就解决了无法对进行状态转移的问题。我们之所以将,也是为了这里的转化。
于是下一个问题是,怎么找到矩阵进行状态转移。
因为状态转移后,应该变成:
然后这里又出现了,再回到那个公式:
(笑)是不是发现了什么?这里直接给出矩阵A,对照着乘一乘,就反应过来了。
(上面那个水印自动加的,这张图不是我的,小声说一下 )
这时,. OK,和矩阵我们现在都找到了,接下来只需要套模板就行了,完美。
题目链接放上面了,很典型的一道题。
其他快速矩阵好题链接:
题解报告:杭电oj3306:Another kind of Fibonacci题解(矩阵快速幂)_issey的博客-CSDN博客
网上有许多常用矩阵快速幂公式,这里就不依次列出了。或许刷题时遇到了某些常用的公式会补充上来。
相关比较经典的题型,边写边补充吧。