快速幂:
1.求5^19,19个5相乘当然可以算出来,但是当指数特别大的时候O(n)就不行了,必须要O(logn)的算法,也就是根据位运算来求解。
19的二进制是(10011)
5^19=5^1 * 5^2 * 5^16;指数对应的二进制如下:
1---00001,2---00010,16---10000
于是我们可以设temp=5(底数),我们求出来的结果是ans;那么我们可以设置一个循环,起始条件是1,边界是n=5(指数的二进制的位数),每循环一次temp就乘一次temp(乘一次就是5^2,两次就是5^4....),同时如果此时的这一位是1,ans就乘上这个temp(根据这个步骤再结合上面的例子就可以清楚的理解代码的原理了):
#include<stdio.h> #define ll long long ll qpow(ll a,ll b) { ll ans=1; while(b) { if(b&1) ans*=a;//当前位置是1就乘上a(底数) a*=a; b>>=1;//b的二进制向后移一位,相当于除于2,比如10011移一次就变成了1001,可以很好的达到上述模拟的效果 } return ans; } ll a,b; int main() { while(scanf("%lld%lld",&a,&b)==2) { printf("%lld\n",qpow(a,b)); } }
矩阵:
a[i]表示斐波那契数列第i项;a[1]=1,a[2]=1;
因为a[i]=a[i-1]+a[i-2],我们就可以尝试能不能通过矩阵相乘来求斐波那契数列,就有了下面的假设;
列出矩阵相乘的结果如下:
两个图对比发现中间矩阵非常容易求得,于是假设成立,把初始矩阵比作a(a[i]=1,a[i-1]=1),中间矩阵比作b,求a[n]就是求a*b^(n-2)然后取第一行第一列;初始矩阵(左)和中间矩阵如下(右):
|1 1| |1 1|
|x y| |1 0|
一般的斐波那契数列用不到x,y,所以x,y可以任意取值,这里x取1,y取0方便计算;原式就变成了a[i]=b^(n-1)然后取第一行第一列.
看到这里会发现,形如a=ax+by+k的式子,给出初始的a[0],b[0]求a[n],都可以构建一个简单的矩阵用乘法来完成模拟,因为用乘法取模拟我们就可以用到快速幂来降低时间复杂度:
#include<bits/stdc++.h> #define ll long long using namespace std; const ll mod=1e9+7; struct node { ll matrix[2][2];//结构体中的矩阵 node() { memset(matrix,0,sizeof(matrix)); }//构造函数,定义一个结构体时自动调用 void one() { matrix[0][0]=1; matrix[1][1]=1; }//单位矩阵 node operator*(node other) { node ans;//记录乘积 for(int i=0; i<2; i++) for(int j=0; j<2; j++) for(int k=0; k<2; k++) { ans.matrix[i][j] += matrix[i][k]*other.matrix[k][j]; ans.matrix[i][j]%=mod; } return ans; }//定义了结构体后就会有这个性质 operator是是对*的重载 }; /* * 相当于p1的一个成员函数 p1*p2相当于对象p1调用函数“operator*”,把对象p2作为一个参数传递给该函数,从而实现了两个对象的相乘。 */ node power(node a,ll b) { node res; res.one();//单位矩阵 while(b) { if(b&1) res=res*a; a=a*a; b>>=1; } return res; }//快速幂求a的b次方 int main() { node temp;//初始矩阵 temp.matrix[0][0]=1; //a[2] temp.matrix[0][1]=1; //a[1] temp.matrix[1][0]=1; temp.matrix[1][1]=0; ll n; while(~scanf("%lld",&n)) { node ans1 = power(temp,n-1);//计算出a[n] printf("%lld\n",ans1.matrix[0][0]); } }