普通快速幂与矩阵快速幂
2016-09-17 15:55
176 查看
普通快速幂
求解n^m%c
先说我们最容易理解的一种方法
这种方法一般情况下都不可用,因为如果m,n较大会超long long,而且此时复杂度为O(m),如果m较大也容易超时
现在我们先来解决超long long的问题,数论中有:积的取余等于取余的积的取余,即 (ab)%c = ( (a%c) * (b%c) ) %c ,所以上面的代码可以改为
此时解决了超long long 的问题,接下来解决超时问题
我们可以先这样想 假如 m 为偶数 那么如果 k=n*n,则上面的代码只需要循环m/2次就好(此时 k 相当于 n ) ,假如m为奇数,k=n*n,那么循环m/2次之后依旧要乘以一个 n ,代码如下:
此时复杂度为O(m/2),依旧可能超时,如上面所说,假如k=n*n,那么循环m/2就可以了,假设m/2为偶数,那么此时让k=k*k,只需要循环m/4次就可以了,如果每循环一下就迭代一次 可以保证复杂度为O( logn ),代码如下(以函数的形式体现):
必要的时候 int 可以换作long long
例题:ZOJ Problem Set - 3549
题意为:给出两个数字 n 和 m (1 <=n<= 100 , 1<=m<=1000000,求 1^m+2^m+3^m+ … +n^m 之和最后剩余的0的个数
代码实现如下:
矩阵快速幂
矩阵快速幂和普通快速幂计算原理差不多,重点在是否能看出是矩阵快速幂的题而且能找出矩阵初始值,此处http://www.matrix67.com/blog/archives/276有能用矩阵快速幂解题的十大经典题目,可以看一下,另外说一下矩阵相乘 :假如存在两个矩阵 A (行数为 i1 列数为 j1)和 B(行数为 i2 列数为 j2 ) 只要满足 j1 == i2 就可以进行相乘 结果矩阵大小为( i1 行 j2 列) 相乘的过程如下
即结果矩阵的第 i 行第 j 列 等于 第一个矩阵的第 i 行乘以(对应相乘)第二个矩阵的第 j 列之和
下面就用一个例子说明一下矩阵快速幂:
例如:HDU 2604 Queuing
题意:n 个人排队,f 表示女性, m 表示男性 ,若队伍中存在序列 fff 或 fmf 序列为 Q 序列,否则序列为 E 序列,问存在多少个 E 序列结果余上m
分析:假如 F( n )表示前n个人排队存在的 E 序列数,那么假如第 n 个人为 m 则F(n-1)即为要求的,假如第n个人为 f 那么无论第 n-1为 m 或为 f 均无法判断,继续往后推,那么后 3 位可能为 fmf fff mmf mff 四种情况,前两种不满足条件,假如是第 3 种 则 F(n-2)即为所求,假如是第 4 种,则还需要考虑下一位 此时只有 mmff满足条件,所以此时 F(n-3)即为所求,所以可知 F(n) = F(n-1) + F(n-2) + F(n-3);
假如不考虑 * 函数重载,可以发现矩阵快速幂和普通快速幂计算过程几乎一样,不过对于特定的题目还是需要注意不少地方,只要多加练习,就会越来越会变通。
求解n^m%c
先说我们最容易理解的一种方法
int ans = 1; for(int i = 1;i <= m;i ++) ans = ans * n; ans = ans%c;
这种方法一般情况下都不可用,因为如果m,n较大会超long long,而且此时复杂度为O(m),如果m较大也容易超时
现在我们先来解决超long long的问题,数论中有:积的取余等于取余的积的取余,即 (ab)%c = ( (a%c) * (b%c) ) %c ,所以上面的代码可以改为
int ans = 1; n=n%c; for(int i = 1;i <= m;i ++) { ans = ans * n; ans = ans%c; }
此时解决了超long long 的问题,接下来解决超时问题
我们可以先这样想 假如 m 为偶数 那么如果 k=n*n,则上面的代码只需要循环m/2次就好(此时 k 相当于 n ) ,假如m为奇数,k=n*n,那么循环m/2次之后依旧要乘以一个 n ,代码如下:
int ans = 1; if(m%2 == 1) { ans = (ans*n)%c; //假如m为奇数可以在前面乘上多出的一项 } n=n%c; for(int i = 1 ; i <= m/2 ; i++ ) { ans = ans*n; ans = ans%c; }
此时复杂度为O(m/2),依旧可能超时,如上面所说,假如k=n*n,那么循环m/2就可以了,假设m/2为偶数,那么此时让k=k*k,只需要循环m/4次就可以了,如果每循环一下就迭代一次 可以保证复杂度为O( logn ),代码如下(以函数的形式体现):
int find(int n , int m) { int ans=1; while( m > 0 ) //当m = 1时 将所有的结果都相乘得出最后结果 ans { if(m%2 == 1) { ans = (ans*n)%c; //m为奇数,可以提前乘以 多出的那一个 } n = (n*n)%c; //无论m为奇还是为偶 都进行迭代 此时m=m/2; m = m/2; } return ans; }
必要的时候 int 可以换作long long
例题:ZOJ Problem Set - 3549
题意为:给出两个数字 n 和 m (1 <=n<= 100 , 1<=m<=1000000,求 1^m+2^m+3^m+ … +n^m 之和最后剩余的0的个数
代码实现如下:
#include<stdio.h> #include<string.h> #define M 1000000000 long long PowerMod(int a,int b) { long long ans = 1,x = a ; while( b ) { if( b%2 == 1 ) ans = (ans*x)%M; b = b/2; x = (x*x)%M; } return ans; } int main() { int n,m,i,ans; long long sum; while(scanf("%d%d",&n,&m) != EOF) { sum = 0 ; for(i = 1;i <= n;i ++) { sum = sum+PowerMod(i,m); } ans = 0; while(sum) { if(sum%10 == 0) ans ++; else break; sum = sum/10; } printf("%d\n",ans); } return 0; }
矩阵快速幂
矩阵快速幂和普通快速幂计算原理差不多,重点在是否能看出是矩阵快速幂的题而且能找出矩阵初始值,此处http://www.matrix67.com/blog/archives/276有能用矩阵快速幂解题的十大经典题目,可以看一下,另外说一下矩阵相乘 :假如存在两个矩阵 A (行数为 i1 列数为 j1)和 B(行数为 i2 列数为 j2 ) 只要满足 j1 == i2 就可以进行相乘 结果矩阵大小为( i1 行 j2 列) 相乘的过程如下
a1 a2 a3 b1 a1*b1 + a2*b2 + a3*b3 a4 a5 a6 * b2 = a4*b1 + a5*b2 + a6*b3 a7 a8 a9 b3 a7*b1 + a8*b2 + a9*b3 3 * 3 3 * 1 3 * 1
即结果矩阵的第 i 行第 j 列 等于 第一个矩阵的第 i 行乘以(对应相乘)第二个矩阵的第 j 列之和
下面就用一个例子说明一下矩阵快速幂:
例如:HDU 2604 Queuing
题意:n 个人排队,f 表示女性, m 表示男性 ,若队伍中存在序列 fff 或 fmf 序列为 Q 序列,否则序列为 E 序列,问存在多少个 E 序列结果余上m
分析:假如 F( n )表示前n个人排队存在的 E 序列数,那么假如第 n 个人为 m 则F(n-1)即为要求的,假如第n个人为 f 那么无论第 n-1为 m 或为 f 均无法判断,继续往后推,那么后 3 位可能为 fmf fff mmf mff 四种情况,前两种不满足条件,假如是第 3 种 则 F(n-2)即为所求,假如是第 4 种,则还需要考虑下一位 此时只有 mmff满足条件,所以此时 F(n-3)即为所求,所以可知 F(n) = F(n-1) + F(n-2) + F(n-3);
F(n) 1 0 1 1 F(n-1) 1 0 1 1 F(4) F(n-1) 1 0 0 0 F(n-2) 1 0 0 0 F(3) F(n-2) = 0 1 0 0 * F(n-3) =......= 0 1 0 0 * F(2) F(n-3) 0 0 1 0 F(n-4) 0 0 1 0 F(1)
代码如下: #include<stdio.h> #include<string.h> struct sat { long long m[4][4]; }; int x; sat operator *(sat a,sat b) // 重载 * { sat c; int i,j,k; for(i=0;i<4;i++) { for(j=0;j<4;j++) { c.m[i][j]=0; for(k=0;k<4;k++) { c.m[i][j] += (a.m[i][k]*b.m[k][j])%x; c.m[i][j]=c.m[i][j]%x; } } } return c; } sat operator^(sat a,int n) //重载 ^ { sat c; //单位矩阵 memset(c.m,0,sizeof(c.m)); c.m[0][0]=c.m[1][1]=c.m[2][2]=c.m[3][3]=1; while(n) { if(n%2==1) c=c*a; n=n/2; a=a*a; } return c; } int main() { sat Init,a; memset(a.m,0,sizeof(a.m)); //矩阵初始化 Init.m[0][0]=9; Init.m[1][0]=6; Init.m[2][0]=4; Init.m[3][0]=2; a.m[0][0]=a.m[0][2]=a.m[0][3]=a.m[1][0]=a.m[2][1]=a.m[3][2]=1; int n; sat s; while(scanf("%d%d",&n,&x)!=EOF) { if(n<=4) { printf("%d\n",Init.m[4-n][0]%x); } else { s=a^(n-4); s=s*Init; printf("%d\n",s.m[0][0]); } } return 0; }
假如不考虑 * 函数重载,可以发现矩阵快速幂和普通快速幂计算过程几乎一样,不过对于特定的题目还是需要注意不少地方,只要多加练习,就会越来越会变通。
相关文章推荐
- 快速幂(普通快速幂、矩阵快速幂)
- Educational Codeforces Round 13——D. Iterated Linear Function(矩阵快速幂或普通快速幂水题)
- HDU 4549 M斐波那契数列(费马小定理,矩阵快速幂,快速幂)
- 稀疏矩阵的压缩储存,稀疏矩阵的普通转置,稀疏矩阵的快速转置
- 稀疏矩阵的(普通/快速)转置
- 垒骰子(25 point(s))(矩阵快速幂+快速幂)
- 快速乘法&快速幂&矩阵快速幂简单讲解
- 稀疏矩阵的普通转置与快速转置算法
- Luogu 3390 【模板】矩阵快速幂 (矩阵乘法,快速幂)
- 快速幂和矩阵快速幂
- POJ 3233 矩阵快速幂(做的快速幂的第一道题你敢信?
- 稀疏矩阵的访问、普通逆置和快速逆置、还原输出以及加法
- Poj 3150 Cellular Automaton(矩阵快速幂, 循环矩阵快速幂)
- 【算法】矩阵的快速幂以及利用快速幂去解题
- 整数的快速幂和矩阵的快速幂
- HDU - 5950 Recursive sequence 矩阵快速幂(由公式推矩阵快速幂简单讲解)
- 【C++】稀疏矩阵的普通转置与快速转置
- 快速幂和矩阵快速幂-模板
- 【递归】普通递归关系 矩阵快速幂
- 算法录 之 快速幂快速乘和矩阵快速幂。