快速幂(取模)
- 快速幂
- 矩阵快速幂
首先我们说一下取模规则:
(ab)%c=((a%c)(b%c))%c
(a±b)%c=((a%c)±(b%c))%c
注:千万千万不要漏掉了最外面的取余,漏掉就错了!!!
快速幂
对于an来说;如果n较小,我们可以直接计算,时间复杂度为O(n),而如果n过大,可能在某些题中就会有超时的风险,所以我们有了快速幂来帮我们优化时间。
大致思路为:
1.将n转换为二进制数相加:如n=7,7的二进制为111,则7=22+ 21 + 20,所以a7=a^ 22 +a^ 21 + a^ 20 ;
2.an = a^(n0+n1* 21+n2 * 22+……+nn * 2n)
an=a^n0 * a^(n1 * 21) * a^(n2 * 22) …… a^(nn * 2n)
3.n为偶数时,an=(a2)^(n/2)
n为奇数:an=a * (a2)^((n-1)/2)
typedef long long ll;
ll quickpow(ll a, ll n, ll mod)//a为底,n为指,mod为模
{
ll ans=1;
a=a%mod;
while(n>0)
{
if(n&1)//当n为奇数时
ans=(ans*a)%mod;//乘上剩下的一个n
n>>=1;//进行二进制位移
a=(a*a)%mod;//将a平方,更新a
}
return ans;
}
矩阵快速幂
矩阵快速幂可以求解常系数齐次线性递推式
类似于:f(n)=f(n-1)+f(n-2)+n+c;(c为常数)
方程有多少项,矩阵就有多少列;
典型:Fibonacci 数列(典型模板):
[F n+1 F n F n F n−1 ]\begin{bmatrix} F~n+1~ & F~n~ \\ F~n~ & F~n-1~ \\ \end{bmatrix}[F n+1 F n F n F n−1 ]=[1110]\begin{bmatrix} 1 & 1 \\ 1 & 0 \\ \end{bmatrix}[1110]n
其中[1110]\begin{bmatrix} 1 & 1 \\ 1 & 0 \\ \end{bmatrix}[1110]n这个矩阵可以由[F n+1 F n ]\begin{bmatrix} F~n+1~ & F~n~ \\ \\ \end{bmatrix}[F n+1 F n ]*[xyzw]\begin{bmatrix} x & y \\ z & w \\ \end{bmatrix}[xzyw]=[F n F n−1 ]\begin{bmatrix} F~n~ & F~n-1~ \\ \\ \end{bmatrix}[F n F n−1 ]求出
矩阵快速幂的函数和快速幂的函数基本一致,就是数的幂变成了矩阵的,也就是底数变成了矩阵,所以要涉及到矩阵的乘法,如:
(1234)\begin{pmatrix} 1 & 2 \\ 3 & 4 \\ \end{pmatrix}(1324) * (1234)\begin{pmatrix} 1 & 2 \\ 3 & 4 \\ \end{pmatrix}(1324) = (1∗1+2∗31∗2+2∗43∗1+4∗33∗2+4∗4)\begin{pmatrix} 1*1+2*3 & 1*2+2*4 \\ 3*1+4*3 & 3*2+4*4 \\ \end{pmatrix}(1∗1+2∗33∗1+4∗31∗2+2∗43∗2+4∗4)
详细内容会在线性代数中学到
矩阵乘法:
void mu(ll a[maxn][maxn],ll b[maxn][maxn])
{
memest(c,0,sizeof(c));
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
for(int k=1;k<=n;k++)
{
c[i][j]=(c[i][j]+a[i][k]*b[k][j]%mod)%mod;//注意取模
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
b[i][j]=c[i][j];
}
[1110]\begin{bmatrix} 1 & 1 \\ 1 & 0 \\ \end{bmatrix}[1110]相当于代码中的a矩阵
快速幂
void qup(ll n)
{
while(n>0)
{
if(n&1)
{
mu(a,b);//a,b为两个矩阵,可在主函数或这个函数中对他们进行初始化
}
mu(a,a);
n>>=1;
}
}