首页 > 其他分享 >快速幂,快速乘,矩阵乘

快速幂,快速乘,矩阵乘

时间:2023-12-21 19:16:02浏览次数:29  
标签:dots int LL 矩阵 long array include 快速

快速幂,快速乘,矩阵乘

快速幂

计算\(a^n(n\geqslant0)\),一般会对答案取个模

例如计算\(5^{11}\),考虑11二进制\((1011)_2\)有\(5^{11} = 5^8*5^2*5^1\)

将n的二进制中为1的位置对应的a的\(2^k\)次幂相乘就能得到最终结果

可以用\(O(\log{n})\)的时间复杂度计算a所有被用到的\(2^k\)

模板

int P = 1e9+7;

int quickpow(long long a, int n)
{
	long long ans = 1;
	for(; n; n >>= 1)
	{
		if(n & 1)
		{
			ans *= a;
			ans %= P;
		}
		a *= a;
		a %= P;
	}
}

快速乘\(O(\log{b})\)

\(a*b\mod P\)

  • \(0\leqslant{a,b}\leqslant{10^{9}},1\leqslant P \leqslant10^9\),开long long直接算
  • \(0\leqslant{a,b}\leqslant{10^{18}},1\leqslant P \leqslant10^9\),由于\((a*b)\%P=(a\%P*b\%P)\%P\)且取完模后并不会报long long,乘之前分别取模
  • \(0\leqslant{a,b}\leqslant{10^{18}},1\leqslant P \leqslant10^{18}\),怎么办?

考虑\(7*11\%P\),\((11)_{2}=1011\),有\(7*11 = 7*8 + 7*2 + 7*1\)

将b的二进制中为1的位置对应的\(2^k*a\)相加就能得到最终结果

模板:

#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
#include <cmath>
#include <stack>
#include <vector>
#include <map>
#include <set>
#include <array>
using namespace std;
#define x first
#define y second
typedef pair<int, int> PII;
typedef long long LL;
LL quickmul(LL a, LL b, LL P)
{
	LL ans = 0;
	a %= P;
	for(; b; b >>= 1)
	{
		if(b & 1) ans += a, ans %= P;
		a += a;
		a %= P;
	}
	return ans;
}
int main()
{	
	LL a,b,P; scanf("%lld%lld%lld",&a,&b,&P);
	printf("%lld",quickmul(a,b,P));

	return 0;
}

矩阵乘法

使用大写字母A,B表示矩阵,\(A_{i,j}\)表示矩阵A中第i行第j列的元素

\[\begin{array}{l} 假设A是一个n行m列矩阵,B为m行k列矩阵,C是一个行k列矩阵 \\ C = A * B \\ C_{i,j} = \sum^{m}_{k=1}A_{i,j}B_{i,j} \end{array} \]

矩阵乘法满足结合律但不满足交换律

\[\begin{array}{l} (A * B) * C = A * (B * C) \\ A * B \neq B * A \end{array} \]

暴力求复杂度\(O(nmk)\)

根据结合律性质,可以使用快速幂快速求出一个矩阵A的n次幂,因为在很多问题中,我们在处理递推公式或动态规划的转移方程时第i项的值可以由之前k项的值推得:

\[\begin{array}{l} f[i] = \sum^{k}_{j = 1} a_{j}*f[i-j] \\ 构造矩阵 \\ A = \left( \begin{array}{cc} 0 & 0 & \dots & 0 & a_{k} \\ 1 & 0 & \dots & 0 & a_{k-1} \\ 0 & 1 & \dots & 0 & a_{k-2} \\ \dots \\ 0 & 0 & \dots & 1 & a_1 \end{array} \right) \\ (f[i-k] \dots f[i-2] f[i-1]) * A = (f[i-k+1] \dots f[i-1] f[i]) \\ (f[1] \dots f[k-1] f[k]) * A^{n-k} = (f[n-k+1] \dots f[n-1] f[n]) \end{array} \]

时间复杂度\(O(k^{3}\log{n})\)

模板:

int n;
LL a[N+1][N+1],f[N+1];
void aa()
{
	LL w[N+1][N+1];
	memset(w,0,sizeof(w));
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= n; j++)
			for(int k = 1; k <= n; k++)
				w[i][j] = a[i][k] * a[k][j],
				w[i][j] %= P;
	memcpy(a,w,sizeof(a));
}
void fa()
{
	LL w[N+1];
	memset(w, 0, sizeof(w));
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= n; j++)
			w[i] += f[j] * a[j][i],
			w[i] %= P;
	memcpy(f,w,sizeof(f));
}
void matrixpow(int k)
{
	for(; k; k >>= 1)
	{
		if(k & 1) fa();
		aa();
	}
}

可以进一步将一定为0的位置省略计算优化

const int N = 200, P = 1e9+10;
int n;
LL a[N+1][N+1],f[N+1];
void aa()
{
	LL w[N+1][N+1];
	memset(w,0,sizeof(w));
	for(int i = 1; i <= n; i++)
		for(int k = 1; k <= n; k++)
			if(a[i][k])
				for(int j = 1; j <= n; j++)
					if(a[k][j])
						w[i][j] += a[i][k] * a[k][j],
						w[i][j] %= P;
	memcpy(a,w,sizeof(a));
}
void fa()
{
	LL w[N+1];
	memset(w, 0, sizeof(w));
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= n; j++)
			w[i] += f[j] * a[j][i],
			w[i] %= P;
	memcpy(f,w,sizeof(f));
}
void matrixpow(int k)
{
	for(; k; k >>= 1)
	{
		if(k & 1) fa();
		aa();
	}
}

例题

image.png
推出来的公式

\[\begin{array}{l} \left(\begin{array}{cc} f_{i-2} & f_{i-1} \end{array} \right) \left(\begin{array}{cc} 0 & 1 \\ 1 & 1 \end{array} \right) = \left(\begin{array}{cc} f_{i-1} & f_{i} \end{array} \right) \end{array} \]

#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
#include <cmath>
#include <stack>
#include <vector>
#include <map>
#include <set>
#include <array>
using namespace std;
#define x first
#define y second
typedef pair<int, int> PII;
typedef long long LL;
const int N = 2, P = 1e9+7;
int n = 2;
int k;
LL f[N+1],a[N+1][N+1];
void aa()
{
	LL w[N+1][N+1];
	memset(w, 0, sizeof(w));
	for(int i = 1; i <= n; i++)
		for(int k = 1; k <= n; k++)
			if(a[i][k])
				for(int j = 1; j <= n; j++)
					if(a[k][j])
						w[i][j] += a[i][k] * a[k][j],
						w[i][j] %= P;
	
	memcpy(a, w, sizeof(a)); 
}
void fa()
{
	LL w[N+1];
	memset(w,0,sizeof(w));
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= n; j++)
			w[i] += f[j] * a[j][i],
			w[i] %= P;
	
	memcpy(f, w, sizeof(f));
}
void output()
{
	for(int i = 1; i <= n; i++)
	{
		for(int j = 1; j <= n; j++)
		{
			cout<<a[i][j]<<" ";
		}
		cout<<endl;
	}
}
void matrixpow(LL k)
{
	for(; k; k>>=1)
	{
		if(k & 1) fa();
		aa();
		// output();
	}
}
int main()
{	
	scanf("%d",&k);
	f[1] = 0, f[2] = 1;
	a[1][1] = 0, a[1][2] = 1;
	a[2][1] = 1, a[2][2] = 1;
	matrixpow(k-1);
	printf("%lld",f[2]);

	return 0;
}

image.png

套路: 求前多少项的和就在矩阵中多加一行一列即可

#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
#include <cmath>
#include <stack>
#include <vector>
#include <map>
#include <set>
#include <array>
using namespace std;
#define x first
#define y second
typedef pair<int, int> PII;
typedef long long LL;
const int N = 3, P = 1e9+7;
int k;
LL f[N+1],a[N+1][N+1];
void aa()
{
	LL w[N + 1][N + 1];
	memset(w, 0, sizeof(w));
	for(int i = 1; i <= N; i++)
		for(int k = 1; k <= N; k++)
			if(a[i][k])
				for(int j = 1; j <= N; j++)
					if(a[k][j])
						w[i][j] += a[i][k] * a[k][j];
	memcpy(a, w, sizeof(a));
}
void fa()
{
	LL w[N + 1];
	memset(w, 0, sizeof(w));
	for(int i = 1; i <= N; i++)
		for(int j = 1; j <= N; j++)
			w[i] += f[j] * a[j][i],
			w[i] %= P;
	memcpy(f, w, sizeof(f));
}
void martixpow(LL k)
{
	for(; k; k >>= 1)
	{
		if(k & 1) fa();
		aa();
	}
}
int main()
{	
	cin>>k;
	f[1] = 0, f[2] = 1, f[3] = 0;
	a[1][1] = 0, a[1][2] = 1, a[1][3] = 0;
	a[2][1] = 1, a[2][2] = 1, a[2][3] = 1;
	a[3][1] = 0, a[3][2] = 0, a[3][3] = 1;
	martixpow(k);
	printf("%lld",f[3]);

	return 0;
}

image.png

使用dp的话k的复杂度太高,使用矩阵的话可以将k的复杂度省掉,此时f的定义则是经过i条边后可以到达的点

#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
#include <cmath>
#include <stack>
#include <vector>
#include <map>
#include <set>
#include <array>
using namespace std;
#define x first
#define y second
typedef pair<int, int> PII;
typedef long long LL;
const int N = 200, P = 1e9 + 10;
int n,m,u,v,k;
LL f[N + 1],a[N + 1][N + 1];
void aa()
{
	LL w[N + 1][N + 1];
	memset(w, 0, sizeof(w));
	for(int i = 1; i <= N; i++)
		for(int k = 1; k <= N; k++)
			if(a[i][k])
				for(int j = 1; j <= N; j++)
					if(a[k][j])
						w[i][j] += a[i][k] * a[k][j],
						w[i][j] %= P;
	memcpy(a, w, sizeof(a));
}
void fa()
{
	LL w[N + 1];
	memset(w, 0, sizeof(w));
	for(int i = 1; i <= N ;i++)
		for(int j = 1; j <= N; j++)
			w[i] += f[j] * a[j][i],
			w[i] %= P;
	memcpy(f, w, sizeof(f));
}
void martixpow(int k)
{
	for(; k; k >>= 1)
	{
		if(k & 1) fa();
		aa();
	}
}
int main()
{	
	scanf("%d%d",&n,&m);
	for(int i = 0; i < m; i++) 
	{
		int x, y; scanf("%d%d", &x, &y);
		a[x][y]++;
	}
	scanf("%d%d%d", &u, &v, &k);
	f[u] = 1;
	martixpow(k);
	printf("%lld", f[v]);

	return 0;
}

image.png

\[\begin{array}{l} f[i] = \sum^{k}_{j = 1} a_{j}*f[i-j] \\ 构造矩阵 \\ A = \left( \begin{array}{cc} 0 & 0 & \dots & 0 & a_{k} \\ 1 & 0 & \dots & 0 & a_{k-1} \\ 0 & 1 & \dots & 0 & a_{k-2} \\ \dots \\ 0 & 0 & \dots & 1 & a_1 \end{array} \right) \\ (f[i-k] \dots f[i-2] f[i-1]) * A = (f[i-k+1] \dots f[i-1] f[i]) \\ (f[1] \dots f[k-1] f[k]) * A^{n-k} = (f[n-k+1] \dots f[n-1] f[n]) \end{array} \]

使用dp求状态\(f[n] = f[n-1] + f[n-m]\),但是效率不高,此时采用矩阵乘除最后一列外,其他如上述构造的矩阵,最后一个位置,n-1位和n-m位有贡献为1,此时只需要右上和右下为1即可

// Problem: D. Magic Gems
// Contest: Codeforces - Educational Codeforces Round 60 (Rated for Div. 2)
// URL: https://codeforces.com/problemset/problem/1117/D
// Memory Limit: 256 MB
// Time Limit: 3000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
#include <cmath>
#include <stack>
#include <vector>
#include <map>
#include <set>
#include <array>
using namespace std;
#define x first
#define y second
typedef pair<int, int> PII;
typedef long long LL;
const int N = 100, P = 1e9+7;
LL n, a[N + 1][N + 1], f[N + 1];
int m;
void aa()
{
	LL w[N + 1][N + 1];
	memset(w, 0, sizeof(w));
	for(int i = 1; i <= m; i++)
		for(int k = 1; k <= m; k++)
			if(a[i][k])
				for(int j = 1; j <= m; j++)
					if(a[k][j])
						w[i][j] += a[i][k] * a[k][j],
						w[i][j] %= P;
	memcpy(a, w, sizeof(a));
}
void fa()
{
	LL w[N + 1];
	memset(w, 0, sizeof(w));
	for(int i = 1; i <= m; i++)
		for(int j = 1; j <= m; j++)
			w[i] += f[j] * a[j][i],
			w[i] %= P;
	memcpy(f, w, sizeof(f));
}
void martixpow(LL k)
{
	for(; k; k >>= 1)
	{
		if(k & 1) fa();
		aa();
	}
}
int main()
{	 
	scanf("%lld%d",&n,&m);
	for(int i = 2; i <= m; i++) a[i][i-1] = 1;
	a[1][m] = 1;
	a[m][m] = 1;
	f[m] = 1;
	martixpow(n);
	printf("%lld\n", f[m]);

	return 0;
}

标签:dots,int,LL,矩阵,long,array,include,快速
From: https://www.cnblogs.com/viewoverlooking/p/17919897.html

相关文章

  • 如何在C#中将float[]快速的转换为byte[]
    昨天喻兄抛出一个问题“如何在C#中将float[]快速的转换为byte[]”。于是开始了尝试。先写了下面的初始化代码usingSystem.Diagnostics;usingSystem.Runtime.InteropServices;Randomrandom=newRandom();//源数组varsrcArray=newfloat[500*1024*1024];//目的数......
  • intellij idea常用快捷键快速生成main方法、for循环、out输出
    1、System.out.println()//输入sout,按下enter键,生成System.out.println()方法.sout--->soutv=System.out.println("变量名="+变量)soutp--->System.out.println("")2、publicstaticvoidmain(String[]args){}//输入psvm,按下enter键,生成main方法.3、for(inti=......
  • .netcore 分布式事务CAP 快速入门
    https://blog.csdn.net/jbossjf/article/details/122590688CAP是一个用来解决微服务或者分布式系统中分布式事务问题的一个开源项目解决方案。可以解决跨服务器的数据一致性、可用性问题。一个简单的列子,如:订单系统创建订单后需要通知邮件通知用户下单成功,解决方案有下面几种:1:创......
  • Modbus转PROFINET网关TS-180快速实现软启动器和马达保护器与西门子PLC的通信
    在钢铁厂的生产过程中,电机作为驱动各种生产机械和辅助设备的关键设备,其正常运行对于生产效率和质量至关重要。为了确保电机的正常运行和使用寿命,通常会使用软启动器和马达保护器等设备,因此监控软启和马达保护器的工作参数成为重点。 福建某钢铁厂,中控室使用S7-1515PLC,实时监控现......
  • 6-4 快速排序
    本题要求实现快速排序的一趟划分函数,待排序列的长度1<=n<=1000。函数接口定义: intPartition(SqListL,intlow,inthigh);其中L是待排序表,使排序后的数据从小到大排列。###类型定义: typedefintKeyType;typedefstruct{KeyType......
  • GEE好文推荐——利用样本点迁移方法快速实现全球范围内1984年至今基于Landsat影像的土
    最近我新发表了一篇新的文章,也就是利用样本点迁移的方法来快速实现全球长时序快速土地分类,本文发布了应用APP,用户可以在线体验使用快速分类的效果。原文链接:Land|FreeFull-Text|RapidLandCoverClassificationUsinga36-YearTimeSeriesofMulti-SourceRemoteSensing......
  • [LeetCode Hot 100] LeetCode74. 搜索二维矩阵
    题目描述思路:二维矩阵坐标变换+二分查找二维矩阵坐标变换:只要知道二维数组的的行数m和列数n,二维数组的坐标(i,j)可以映射成一维的index=i*n+j;反过来也可以通过一维index反解出二维坐标i=index/n,j=index%n。(n是列数)把二维数组matrix的元素访问抽象成在......
  • 快速下载并发布
    rm/web_sites/digg_apis_svc/SGT.DiggApis.Svcwget-O/web_sites/digg_apis_svc/SGT.DiggApis.Svchttp://127.0.0.1:9003/digg_svc/SGT.DiggApis.Svcchmod+x/web_sites/digg_apis_svc/SGT.DiggApis.Svcsystemctlrestartdigg.api.servicesystemctlstatusdigg.api.serv......
  • quartus ii快速写入管脚分配方法
    1.创建.tcl文件set_location_assignmentPIN_92-toXD[4]set_location_assignmentPIN_47-toXD[3]set_location_assignmentPIN_48-toXD[2]set_location_assignmentPIN_70-toXD[1]根据自己的引脚分配修改<>中内容即可,set_location_assignment<PIN_XX>-to2.导入t......
  • Linux服务器快速安装Redis-6.0
    最近开始体验FastGPT开源知识库问答系统,用他们试着开发调试一些小助手。这中间需要使用到Redis,就在自己服务器上进行了安装,特此记录下。环境说明:阿里云ECS,2核8G,X86架构,CentOS7.9操作系统。选择版本1.打开Redis官网下载页面,可以选择需要的版本下载。我这里选择的是6.2.14版本......