目录
快速幂
小规律:(a*b)%p (a%p*b%p)%p;
求 暴力求解的话需要把m乘上n次,复杂度为O(n),假设n为1e7的话会超时;
如何把这个时间优化到的级别呢?
总体思路就是,不断的把底数平方,从而把指数除以2;
主要有两个核心操作,预设结果为 ans=1
第一步 ,如果n为偶数假设 化成 ,这样复杂度就变成了原来的一半。按照这个思想,每次把底数平方那复杂度就减半,就会变成log级别的了;不过这只是指数n偶数的情况,如果n是奇数呢?
第二步,如果n变成了奇数,那么我们可不可以把底数分离出来一个,变成,然后再按照第一步变成,这样先把分离出来的底数m放在一遍(变成ans=ans*m),然后对继续进行这两步操作;
接下来用一个示例演示:计算
预设结果ans=1
- n=10是偶数,执行第一步,变成,也就是
- n=5是奇数,执行第二步,分离出来一个底数9,ans=ans*9;变成
- n=4是偶数,执行第一步,变成,也就是
- n=2是偶数,执行第一步,变成,也就是
- n=1是奇数,执行第二步,分离出来一个6561,ans=ans*6561;变成
- n=0结束,ans=ans*9*6561=59049
贴一个偷来的动态图~~
模板:(通常为了防止结果太大,题目会要求结果进行取模运算,为了保险可以把int换成long long)
求 m^n mod p,时间复杂度 O(logk)。
int qmi(int m, int n, int p)
{
int ans = 1;
while (n)
{
if (n&1) ans = ans * m % p;//n是奇数时,把底数分离出来一个放在结果里
m = m * m % p; //底数平方
n >>= 1; //指数除以2
}
return ans;
}
矩阵快速幂
学了快速幂,矩阵快速幂就更简单了。首先矩阵的幂形式是:假设A是一个矩阵,求;
也就是n个A矩阵相乘,和快速幂原理一样,只需要把整数相乘,换成矩阵相乘即可;
矩阵相乘图示:
直接上板子:
typedef long long ll;
const int mod=9973;//定义模数,根据题目而定
struct node
{
ll ma[11][11]; //定义一个矩阵,矩阵的大小根据题目而定
};
ll m;
node mul(node a,node b)//自定义矩阵的乘法矩阵的乘法
{
node ans;
memset(ans.ma,0,sizeof ans.ma);//对矩阵初始化
for(int i=1;i<=10;i++)
for(int j=1;j<=10;j++)
for(int k=1;k<=10;k++)
ans.ma[i][j]=(ans.ma[i][j]+a.ma[i][k]*b.ma[k][j])%m;//注意矩阵运算中,乘法处处取模,防止溢出
return ans;
}
node pow(node a,int b)//矩阵的快速幂
{
node ans;
memset(ans.ma,0,sizeof ans.ma);
for(int i=1;i<=10;i++) ans.ma[i][i]=1;//对矩阵的初始化,初始化为单位矩阵
while(b)
{
if(b&1) ans=mul(ans,a);
a=mul(a,a);
b>>=1;
}
return ans;
}
其实就是整数乘法换成了矩阵乘法;
中间矩阵化为初等矩阵是因为初等矩阵乘以任何矩阵都等于原矩阵:
(这里的初等矩阵E,就相当于快速幂里面保存答案的ans)
使用矩阵快速幂主要是用来解决递推式的问题,核心在于构造初始矩阵
举个例子:
斐波那契数列:已知,通项:,求第n项,结果对p取模;
先构造矩阵,和矩阵快速幂扯上关系。构造 A x M = N ,也就是 ,答案在矩阵N的左上角,也就是第一个元素。
那么矩阵A构造出来就是,也就是,
递推一步得
所以归纳得
现在已经知道了,只需要用矩阵快速幂求出来,然后和相乘,得到,就可以得到;
如有错,留评~