杭电上有一道十分让初学者十分蛋疼的题 a^b%m,看似很简单,但题目要求b的范围是(0,1000000000],a是32位整数范围,m是小于40000的整数。咋一看这题,貌似要用高精度。但是赤裸裸的用高精度的话,在空间复杂度以及时间复杂度上都是伤不起的!!
让我们来换个思路,有一定数学基础的人都知道,(a*b)%m 是等价于 a%m * b%m的,这样好了,可以不用高精度了,但是完全模拟的话,在规定时间内是不可能出解的。
我们再想想,是否可以减小计算量呢?有了!我们可以将 a^b 分解成为 t1*a^1+t2*a^2+t3*a^3+.....(ti=0 或者 1),我们预处理下,算出a^2i,然后将b变为二进制,乘以相应的项,我们就得到了最后的结果。运算次数大大减少了,这样是可以在规定时间内出解的。
如果我们觉得上面的方法太麻烦的话,下面的递归代码就十分简单了。
#include<iostream>
using namespace std;
int run(int a,int b,int m){
int t;
if (b==1) return a%m;
if (b&1){
t=run(a,b/2,m);
return ((t*t%m)*a)%m;
}else{
t=run(a,b/2,m);
return (t*t)%m;
}
}
int main(){
int a,b,m;
cin >> a >> b >> m ;
cout << run(a,b,m);
system("pause");
return 0;
}
这其实是利用了一个分治的思想,要想求a^b1,先求出a^b2(其中b2=b1/2),而要求a^b2,又得求出a^b3(其中b3=b2/2)..........直到bi =1。整个问题的时间复杂度就变得非常低了。最多几十次基本操作就能得到结果(有木有感觉很神奇)。其实我们还可以将这个算法求斐波拉契数列的第10000000项对m取余,这样用到矩阵的乘法,下面给出具体的矩阵推导以及代码。
#include<iostream>
using namespace std;
class Matrix{
public:
int width,height;
Matrix(){
}
Matrix(int hei,int wid){
width = wid; height = hei;
v = new int*[height];
for (int i=0;i<height;i++)
v[i] = new int[width];
}
void set(int hei,int wei){
v = new int*[height];
for (int i=0;i<height;i++)
v[i] = new int[width];
}
int** v;
friend Matrix operator*(Matrix a,Matrix b){
Matrix res(a.height,a.width);
//确保数据正确
//if (a.width!=b.height) retutn res;
for (int i=0;i<a.height;i++){
for (int j=0;j<a.width;j++){
int ans = 0;
for (int k=0;k<a.width;k++){
ans += a.v[i][k]*b.v[k][j];
}
res.v[i][j] = ans;
}
}
return res;
};
friend Matrix operator%(Matrix a,int m){
Matrix ans(a.height,a.width);
for (int i=0;i<a.height;i++)
for (int j=0;j<a.width;j++)
ans.v[i][j] = a.v[i][j] % m;
return ans;
}
};
Matrix a(2,2);
Matrix run(int b,int m){
Matrix t;
if (b==1) return a%m
;
if (b&1){
t=run(b/2,m)%m;
return ((t*t)*a)%m;
}else{
t=run(b/2,m)%m;
return (t*t)%m;
}
}
int main(){
int n,m;
while(1){
cin >> n >> m;
a.v[0][0] = 0; a.v[0][1] = 1; a.v[1][0] = 1; a.v[1][1] = 1;
Matrix ans;
ans = run(n,m);
for (int i=0;i<ans.height;i++){
for (int j=0;j<ans.width;j++)
cout << ans.v[i][j] << " ";
cout << endl;
}
}
system("pause");
return 0;
}
分治就是分而治之的缩写,分治的思想并不局限于这道题,它能极大的提高算法的效率。所以掌握它是十分必要的。
- 大小: 68.4 KB
分享到:
相关推荐
void MERGE(int *A, int b, int m, int e) { // ... } ``` **合并排序**: 合并排序是一种基于分治的排序算法。它将待排序序列分为两个子序列,分别进行排序,然后将两个有序子序列合并成一个完整的有序序列...
快速幂算法的核心思想是利用幂运算的乘法性质:\(a^{m+n} = a^m \cdot a^n\) 和指数的分治策略。这种算法将指数表示为二进制形式,然后通过不断平方和乘法来求解。以下是快速幂的基本步骤: 1. **初始化**:给定...
第K小数,快速幂,下载之后负责答疑哦 int cmp(int x,int y) { return x; } void Swap() { swap(a[i],a[j]);... if(a[i]>a[j]) ... if(b[l]==a[i]) { cout; break; } } else Operation(START,i-1); }
- **扩展欧几里得算法**:不仅可以求出两数的最大公约数,还可以求出一组解使得ax + by = gcd(a, b)成立。 #### 质因数分解 - **基本思想**:将一个正整数分解为多个质数的乘积。 - **应用**:密码学、加密算法等...
算法分析与设计课件:分治法 分治法是一种常用的算法设计方法,它通过将问题分解成若干个子问题,然后求解子问题,由此得到原问题的解。分治法的基本思想是“分而治之”,即将问题分解成多个子问题,然后将子问题的...
在矩阵乘法的分治法中,首先将矩阵 A 和 B 分成 4 个子矩阵:A11、A12、A21、A22 和 B11、B12、B21、B22。然后,对每个子矩阵进行相乘,得到 C11、C12、C21、C22。最后,将 C11、C12、C21、C22 合并得到最终的矩阵 C...
- 当 \( m \bmod n = 0 \),\( gcd(m, n) = n \); - 否则,\( gcd(m, n) = gcd(n, m \bmod n) \)。 #### GCD 的 C++ 实现: ```cpp int gcd(int m, int n) { if (m % n == 0) return n; else return gcd(n, m % ...
用数学符号表示,若A是m×n矩阵,B是n×p矩阵,则C将是m×p矩阵,其元素c[i][j]由以下公式给出: \[ c[i][j] = \sum_{k=1}^{n} a[i][k] \cdot b[k][j] \] 矩阵乘法在计算机科学中有多种实现方法,包括朴素的循环...
在数学中,如果一个矩阵A有m行n列,另一个矩阵B有n行p列,那么它们可以相乘得到一个m行p列的新矩阵C。每个新矩阵的元素c[i][j]是由A的第i行和B的第j列对应元素的乘积之和构成的,即: \[ c[i][j] = \sum_{k=1}^{n} ...
在M-ary乘方算法中,我们关注的是在模意义下的指数运算,即给定一个基数b,一个指数e和一个模数m,我们寻找\( b^e \)对m取模的结果。这个结果被称为模指数。M-ary算法的关键在于它可以将指数分解成M的不同幂的组合,...
本文主要介绍了使用分治策略实现线性时间选择算法的设计和实现。该算法使用了分治策略,通过递归地将原始问题分解成小规模的问题,最后合并结果以获得所需的答案。 分治策略 分治策略是一种常用的算法设计技术,它...
本实验报告的主要内容是关于算法设计的实验报告,涵盖了递归与分治策略的概念和实现。实验的主要目的在于熟悉递归的概念和掌握设计有效算法的分治策略。 一、实验目的 本实验的主要目的是: 1. 熟悉递归的概念 2....
例如,求a的b次方模m,可以通过以下步骤: 1. 初始化结果res为1。 2. 将b转换为二进制表示,例如b = b1...bn,其中bi是二进制位。 3. 从低位到高位遍历二进制位bi: - 如果bi为1,则将res更新为res * a模m。 - 将...
矩阵相乘的基本概念是,两个矩阵A(m×n)和B(n×p)可以相乘得到一个新的矩阵C(m×p),其中C中的每个元素c[i][j]是由A的第i行和B的第j列对应元素的乘积之和计算得出的。这个过程可以表示为: \[ c[i][j] = \sum...
m := (a + b) div 2; if x = L[m] then return(m) else if x > L[m] then return(Binary_Search(L, m + 1, b, x)) else return(Binary_Search(L, a, m - 1, x)); end; end; ``` 这段代码首先检查是否还有待...
- (b) 可以使用快速选择算法,它是一种基于快速排序的算法,可以在最坏情况下达到O(n log n)的时间复杂度来寻找权重中位数。 - (c) 分治算法可以将问题分解为两半,分别找到左右两边的权重中位数,然后比较确定...
if a < b t = x1(b+1); x1(b+1) = x1(a+1); x1(a+1) = t; % 交换位置 end; k = nv2; while k <= b b = b - k; k = k / 2; end; b = b + k; end; ``` 此段代码实现了输入序列的倒序位操作,这是按时间抽取...
**实验报告:分治法实现归并排序算法** 一、实验背景 归并排序是一种基于分治策略的高效排序算法,其主要思想是将大问题分解为小问题,然后逐个解决,最后再将小问题的解组合成大问题的解。本实验旨在通过实际编程...
假设我们有两个二维矩阵A(m×n)和B(n×p),它们的乘积C将是m×p的矩阵,其中每个元素C[i][j]是A[i][k]*B[k][j]的和,k从0到n-1。传统的矩阵乘法算法需要O(mnp)次运算。然而,通过分治法,我们可以将其改进为更高效的...