快速幂

思想

我们要让计算机很快地计算出aba^b
可以利用乘方的性质:axay=ax+ya^xa^y=a^{x+y}
我们要求aba^b,可以将bb拆成二进制数。比如b=(11)10=(1011)2b=(11)_{10}=(1011)_2,从左至右,每一个11,分别代表8,2,18,2,1,可以计算a11=a8a2a1a^{11}=a^8a^2a^1
可以得出,如果bb在二进制上的第nn位是11,我们就把答案乘上对应的a2na^{2^n}
在快速幂中,我们通过aa的不断自乘,得到a1,a2,a4...a^1,a^2,a^4...,通过bb的右移(>>),自右向左取得bb的二进制位,用按位与运算(&)判断每一位是否为11

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include<iostream>
using namespace std;
typedef long long ll;
ll quick_pow(ll a,ll b)
{
ll res=1;
while(b)
{
if(b&1) res*=a;
b>>=1;
a=a*a;
}
return res;
}
int main()
{
ios::sync_with_stdio(false);
ll a,b;
cin>>a>>b;
cout<<quick_pow(a,b);
return 0;
}

当然,一般会加上取余运算,计算$a^b mod $ pp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include<iostream>
using namespace std;
typedef long long ll;
const ll p=1e9+7;
ll quick_pow_mod(ll a,ll b)
{
ll res=1;
while(b)
{
if(b&1)
{
res*=a;
res%=p;
}
b>>=1;
a=a*a;
a%=p;
}
return res;
}
int main()
{
ios::sync_with_stdio(false);
ll a,b;
cin>>a>>b;
cout<<quick_pow_mod(a,b);
return 0;
}

矩阵快速幂

含义

AA是一个nn阶方阵,我们需要快速地计算AbA^b

类比

矩阵快速幂与数的快速幂类似,只需要将快速幂代码的数,换成矩阵即可。

实现

①利用结构体定义矩阵,成员为一个n×nn×n的二维数组。
②重载‘*’,实现矩阵的乘法${(AB)_{ij}} = \sum\limits_{k = 1}^n {{a_{ik}}{b_{kj}}} $。
③套快速幂的模板。

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=100;
ll n,b;
struct matrix
{
ll m[N][N];
matrix()//构造函数
{
memset(m,0,sizeof(m));
}
};
matrix operator *(const matrix &x,const matrix &y)
{
matrix res;
int i,j,k;
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
for(k=1;k<=n;k++)
{
res.m[i][j]+=x.m[i][k]*y.m[k][j];
}
}
}
return res;
}
matrix quick_matrix_pow_mod(matrix a,ll b)
{
matrix res;
int i;
for(i=1;i<=n;i++) res.m[i][i]=1;//单位矩阵
while(b)
{
if(b&1) res=res*a;
a=a*a;
b>>=1;
}
return res;
}
int main()
{
ios::sync_with_stdio(false);
cin>>n>>b;
int i,j;
matrix a;
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
cin>>a.m[i][j];
}
}
matrix ans=quick_matrix_pow_mod(a,b);
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
cout<<ans.m[i][j];
if(j<n) cout<<' ';
}
cout<<endl;
}
return 0;
}

当然,一般会要求对矩阵的每一个元素取余,只需要在重载‘*’时对元素取余即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=100;
const ll p=1e9+7;
ll n,b;
struct matrix
{
ll m[N][N];
matrix()//构造函数
{
memset(m,0,sizeof(m));
}
};
matrix operator *(const matrix &x,const matrix &y)
{
matrix res;
int i,j,k;
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
for(k=1;k<=n;k++)
{
res.m[i][j]+=x.m[i][k]*y.m[k][j];
res.m[i][j]%=p;
}
}
}
return res;
}
matrix quick_matrix_pow_mod(matrix a,ll b)
{
matrix res;
int i;
for(i=1;i<=n;i++) res.m[i][i]=1;//单位矩阵
while(b)
{
if(b&1) res=res*a;
a=a*a;
b>>=1;
}
return res;
}
int main()
{
ios::sync_with_stdio(false);
cin>>n>>b;
int i,j;
matrix a;
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
cin>>a.m[i][j];
}
}
matrix ans=quick_matrix_pow_mod(a,b);
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
cout<<ans.m[i][j];
if(j<n) cout<<' ';
}
cout<<endl;
}
return 0;
}

矩阵加速数列递推 (e.g. Fibonacci Sequence)

矩阵的递推思想

FibonacciFibonacci SequenceSequence的第nn项为fnf_n,f0=0,f1=1f_0=0,f_1=1,那么有

fn=fn1×1+fn2×1,fn1=fn1×1+fn2×0(n2)f_n =f_{n - 1} ×1+ f_{n - 2}×1, f_{n-1}=f_{n-1}×1+f_{n-2}×0(n \geqslant 2)

可以写成矩阵的乘法

,于是可得
AnA^n可由矩阵快速幂得到, 最后利用矩阵乘法定义得到fn=A21nf_n=A^n_{21},于是就这样在O(logn)O(logn)的时间实现了数列递推。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=100;
const ll p=1e9+7;
const int n=2;
struct matrix
{
ll m[N][N];
matrix()//构造函数
{
memset(m,0,sizeof(m));
}
};
matrix operator *(const matrix &x,const matrix &y)
{
matrix res;
int i,j,k;
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
for(k=1;k<=n;k++)
{
res.m[i][j]+=x.m[i][k]*y.m[k][j];
res.m[i][j]%=p;
}
}
}
return res;
}
matrix quick_matrix_pow(matrix a,ll b)
{
matrix res;
int i;
for(i=1;i<=n;i++) res.m[i][i]=1;//单位矩阵
while(b)
{
if(b&1) res=res*a;
a=a*a;
b>>=1;
}
return res;
}
int main()
{
ios::sync_with_stdio(false);
int k;
cin>>k;//输出第k项的fib
int i,j;
matrix a;
a.m[1][1]=a.m[1][2]=a.m[2][1]=1;
a.m[2][2]=0;
matrix ans=quick_matrix_pow(a,k);
cout<<ans.m[2][1]%p;
return 0;
}