矩阵乘法
可以把矩阵理解为二维数组,数存在里面,矩阵乘法的规则:A*B=C
实现代码如下:
const int N=50;
int c[N][N];
void multi(int a[N][N],int b[N][N],int n);
{
memset(c,0,sizeof(c));
for(int i=1;i<=n;i++)
{
for(int k=1;k<=n;k++)
{
if(a[i][k]==0) continue;
for(int j=1;j<=n;j++)
{
c[i][j]+=a[i][k]*b[k][j];
}
}
}
}
快速幂和矩阵快速幂
- 利用位运算判断奇偶性
if(i&1)
{
奇数....
}
else
{
偶数...
}
- 程序终止问题
exit(0):正常运行程序并退出程序;
exit(1):非正常运行导致退出程序;
return():返回函数,若在主函数中,则会退出函数并返回一值。
快速幂
- 快速幂原理
如果当前的指数是偶数,我们把指数拆成两半,得到两个相同的数,然后把这两个相同的数相乘,可以得到原来的数;
如果当前的指数是奇数,我们把指数拆成两半,得到两个相同的数,此时还剩余一个底数,把这两个相同的数和剩余的底数这三个数相乘,可以得到原来的数。 - 当n>0代码如下
long long res = 1;
// 进行快速幂运算,n 为当前的指数值,n 为 0 的时候运算结束
while (n) {
// 用位运算的方式判断 n 是否为奇数,速度更快,等价于 n%2
if (n & 1) {
// 如果 n 是奇数,那么需要将 x 存入运算结果中
res *= x;
}
// 更新当前的 x 的值
x *= x;
// 用位运算的方式进行 n/2,速度更快,等价于 n/=2
n >>= 1;
}
完整的代码如下:
#include <iostream>
#include <cstdlib>
using namespace std;
// 使用快速幂求出 x^n 的值并返回,不考虑高精度,请控制参数范围
double myPow(double x, int n) {
// 任何不是 0 的数的 0 次幂为 1
if (x && n == 0) {
return 1;
} else if (x == 0 && n == 0) {
exit(1);
}
// 如果 n 是负数,那么返回结果要进行处理
bool judge = false;
if (n < 0) {
judge = true;
n = -n;
}
double res = 1;
while (n) {
// 用位运算的方式判断 n 是否为奇数,速度更快,等价于 n%2
if (n & 1) {
res =res*res%p;
}
x = x*x%p;
// 用位运算的方式进行 n/2,速度更快,等价于 n/=2
n >>= 1;
}
// n 是负数?1.0/res 否则 res
if(judge)
{
return 1.0/res;
}
else
{
return res;
}
}
int main() {
double x;
int n;
while (cin >> x >> n) {
cout << myPow(x, n) << endl << endl;
}
return 0;
}
矩阵快速幂模板代码如下
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define MAXN 200
#define MOD 1000000007
using namespace std;
typedef long long ll;
int A[MAXN][MAXN];
int res[MAXN][MAXN];
ll tmp[MAXN][MAXN];
int n; ll k;
void mulit(int a[][MAXN],int b[][MAXN])
{
memset(tmp,0,sizeof(tmp));
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
for(int k=1;k<=n;k++)
{
tmp[i][j]=((ll)a[i][k]*b[k][j]%MOD+tmp[i][j])%MOD;
}
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
a[i][j]=(int)tmp[i][j];
}
}
}
void Pow(int a[][MAXN],ll k)
{
memset(res,0,sizeof(res));
for(int i=1;i<=n;i++)
{
res[i][i]=1;
}
while(k)
{
if(k&1)
{
mulit(res,a);
}
mulit(a,a);
k>>=1;
}
}
int main()
{
scanf("%d%lld",&n,&k);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
scanf("%d",&A[i][j]);
}
}
Pow(A,k);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
printf("%d ",res[i][j]);
}
cout<<endl;
}
}
(代码借用了https://blog.csdn.net/wust_zzwh/article/details/52058209)
利用矩阵快速幂求斐波那契数列
利用矩阵快速幂求递推数列的关键是找到合适的递推关系式
如此大佬博客中所推蓝桥杯 算法提高 递推求值(矩阵快速幂)详解
利用矩阵快速幂求斐波那契数列的模板题链接P1939 【模板】矩阵加速(数列)
模板题代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define MAXN 200
#define MOD 1000000007
#define ll long long
using namespace std;
ll n;
ll A[MAXN][MAXN];
ll B[MAXN][MAXN];
ll res[MAXN][MAXN];
ll tmp[MAXN][MAXN];
void mulit(ll a[][MAXN],ll b[][MAXN])
{
memset(tmp,0,sizeof(tmp));
for(int i=1;i<=6;i++)
{
for(int j=1;j<=6;j++)
{
for(int k=1;k<=6;k++)
{
tmp[i][j]=((a[i][k]*b[k][j])%MOD+tmp[i][j])%MOD;
}
}
}
for(int i=1;i<=6;i++)
{
for(int j=1;j<=6;j++)
{
a[i][j]=tmp[i][j];
}
}
}
void pow(ll a[][MAXN],ll k)
{
memset(res,0,sizeof(res));
for(int i=1;i<=6;i++)
{
res[i][i]=1;
}
while(k)
{
if(k&1)
{
mulit(res,a);
// printf("k=%lld
",k);
// printf("res=%lld
",res[1][1]);
}
mulit(a,a);
k>>=1;
}
}
int main()
{
scanf("%lld",&n);
if(n==1||n==2)
{
printf("1");
return 0;
}
A[1][1]=1;
A[1][2]=1;
A[2][1]=1;
A[2][2]=0;
B[1][1]=1;
B[2][1]=1;
pow(A,n-2);
// cout<<1;
mulit(res,B);
printf("%lld ",res[1][1]);
return 0;
}
类似题及代码如下:
P1939 【模板】矩阵加速(数列)
代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAXN 7
#define MOD 1000000007
#define ll long long
using namespace std;
ll n;
ll A[7][7];
ll B[7][7];
ll res[7][7];
ll tmp[7][7];
void mulit(ll a[][MAXN],ll b[][MAXN])
{
memset(tmp,0,sizeof(tmp));
for(int i=1;i<=6;i++)
{
for(int j=1;j<=6;j++)
{
for(int k=1;k<=6;k++)
{
tmp[i][j]=((a[i][k]*b[k][j])%MOD+tmp[i][j])%MOD;
}
}
}
for(int i=1;i<=6;i++)
{
for(int j=1;j<=6;j++)
{
a[i][j]=tmp[i][j];
}
}
}
void pow(ll a[][MAXN],ll k)
{
memset(res,0,sizeof(res));
for(int i=1;i<=6;i++)
{
res[i][i]=1;
}
while(k)
{
if(k&1)
{
mulit(res,a);
// printf("k=%lld
",k);
// printf("res=%lld
",res[1][1]);
}
mulit(a,a);
k>>=1;
}
}
int main()
{
ll T;
scanf("%lld",&T);
while(T--)
{
scanf("%lld",&n);
if(n==1||n==2||n==3)
{
printf("1
");
continue;
}
A[1][1]=1;
A[1][2]=0;
A[1][3]=1;
A[2][1]=1;
A[2][2]=0;
A[2][3]=0;
A[3][1]=0;
A[3][2]=1;
A[3][3]=0;
B[1][1]=1;
B[2][1]=1;
B[3][1]=1;
pow(A,n-2);
mulit(res,B);
printf("%lld
",res[2][1]);
}
}
有一个小结论叫做gcd(F(n),F(m))=F(gcd(n,m))
P1306 斐波那契公约数
代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define MAXN 9
#define MOD 100000000
#define ll long long
using namespace std;
ll n,m;
ll A[MAXN][MAXN];
ll B[MAXN][MAXN];
ll res[MAXN][MAXN];
ll tmp[MAXN][MAXN];
void mulit(ll a[][MAXN],ll b[][MAXN])
{
memset(tmp,0,sizeof(tmp));
for(int i=1;i<=5;i++)
{
for(int j=1;j<=5;j++)
{
for(int k=1;k<=5;k++)
{
tmp[i][j]=((a[i][k]*b[k][j])%MOD+tmp[i][j])%MOD;
}
}
}
for(int i=1;i<=5;i++)
{
for(int j=1;j<=5;j++)
{
a[i][j]=tmp[i][j];
}
}
}
void pow(ll a[][MAXN],ll k)
{
memset(res,0,sizeof(res));
for(int i=1;i<=5;i++)
{
res[i][i]=1;
}
while(k)
{
if(k&1)
{
mulit(res,a);
}
mulit(a,a);
k>>=1;
}
}
int main()
{
scanf("%lld%lld",&n,&m);
A[1][1]=1;
A[1][2]=1;
A[2][1]=1;
A[2][2]=0;
B[1][1]=1;
B[2][1]=1;
ll num;
num=__gcd(n,m);
// printf("num=%lld",num);
if(num==1||num==2)
{
printf("1");
return 0;
}
pow(A,num-2);
mulit(res,B);
printf("%lld",res[1][1]);
}
乘法逆元
乘法逆元的几种计算方法
线性推核心代码如下:
inv[1]=1;
printf("1
");
for(int i=2;i<=n;i++)
{
inv[i]=(MOD-MOD/i)*inv[MOD%i]%MOD;
printf("%lld
",inv[i]);
}
拓展欧几里得算法求乘法逆元代码如下
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
void exgcd(int a,int b,int &x,int &y)
{
if(b==0)
{
x=1;
y=0;
return;
}
else
{
int tx,ty;
exgcd(b,a%b,tx,ty);
y=tx-(a/b)*ty;
x=ty;
return;
}
}
int main()
{
int n,p;
scanf("%d%d",&n,&p);
for(int i=1;i<=n;i++)
{
int x,y;
exgcd(i,p,x,y);
if(x<0)
{
x=x+p;
}
printf("%d
",x);
}
}
费马小定理代码如下:
ll Pow(ll a, ll k)
{
ll res=1;
while(k)
{
if(k&1)
{
res=res*a%MOD;
}
a=a*a%MOD;
k>>=1;
}
return res;
}
模板题链接:P3811 【模板】乘法逆元
拓展题链接:P2613 【模板】有理数取余
拓展题代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define ll long long
#define MAXN 100010
using namespace std;
const int MOD=19260817;
ll a,b;
char str[MAXN];
ll Pow(ll a,ll k)
{
ll res=1;
while(k)
{
if(k&1)
{
res=res*a%MOD;
}
a=a*a%MOD;
k>>=1;
}
return res;
}
int main()
{
scanf( "%s", str+1 );
for( int i = 1, en = (int)strlen( str+1 ); i <= en; ++i )
a = ( a * 10 + str[i] - '0' ) % MOD;
scanf( "%s", str+1 );
for( int i = 1, en = (int)strlen( str+1 ); i <= en; ++i )
b = ( b * 10 + str[i] - '0' ) % MOD;
if(b==0)
{
printf("Angry!");
return 0;
}
ll ans=(Pow(b,MOD-2)%MOD*a)%MOD;
printf("%lld",ans);
}