快速幂取模:
原理:合并相同项,减少计算
计算(a^b)%c当b很大的时候用,朴素算法复杂度为n,快速幂复杂度为logn
以下为二进制优化后的代码:
ll quick_pow_mod(ll a,ll b,ll c)
{
ll res=1;
while(b)
{
if(b & 1)
{
res=(res*a)%c;
}//
a=(a*a)%c;
b=b>>1;
}
return res;
}
快速幂有两个作用 一是降低复杂度,二是防止数据过大溢出。
同理,也有快速乘,但是我觉得这个玩意用的机会应该很少...
ll quick_mul(ll a,ll b,ll c)//类比快速幂
{
ll res=0;
while(b)
{
if(b&1)
{
res=(res+a)%c;
}
a=(a+a)%c;
b=b>>1;
}
return res;
}
矩阵快速幂取模:
矩阵乘法和快速幂的结合,可以快速计算矩阵a的n次幂,把快速幂中的乘法改成矩阵乘法就行。
ps:我就先默认矩阵为二阶方阵了,其他情况改成N就好。
struct mat
{
ll a[2][2];
mat()
{
memset(a,0,sizeof(mat));//这一步不要忘记
}
};
mat multiple(mat x,mat y)//模拟矩阵乘法,返回res矩阵
{
mat res;
for(int i=0;i<2;i++)//枚举res的行
{
for(int j=0;j<2;j++)//枚举res的列
{
for(int k=0;k<2;k++)
{
res.a[i][j]=((res.a[i][j]%mod)+((x.a[i][k]*y.a[k][j])%mod))%mod;
}
}
}
return res;
}
mat quick_pow(mat base,int n)
{
mat ans;
for(int i=0;i<2;i++)
{
ans.a[i][i]=1;//初始化为单位矩阵
}
while(n)
{
if(n&1)
{
ans=multiple(ans,base);//如果n这一位上为1,更新ans
}
base=multiple(base,base);//更新base
n=n>>1;
}
return ans;
}
应用:
如斐波拉切数列:已知f(n)=f(n-1)+f(n-2),由矩阵乘法可以通过中间矩阵,找到f(n)和已知项的关系
然后只要对转移矩阵快速幂就行了。
贴一道UVA-10689,斐波拉切数列的变形,给定前两项,求第n项对一个数取模,矩阵快速幂模版题
#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstring>
typedef long long ll;
using namespace std;
int mod;
int a,b,n,m;//第一项为a 第二项为b 求第n项的后m位
struct mat
{
ll a[2][2];
mat()
{
memset(a,0,sizeof(mat));
}
};
mat multiple(mat x,mat y)//模拟矩阵乘法,返回值res仍为矩阵
{
mat res;
memset(res.a,0,sizeof(res.a));
for(int i=0;i<2;i++)//枚举行
{
for(int j=0;j<2;j++)//枚举列
{
for(int k=0;k<2;k++)
{
res.a[i][j]=((res.a[i][j]%mod)+((x.a[i][k]*y.a[k][j])%mod))%mod;
}
}
}
return res;
}
ll quick_power(int n)
{
mat base,res;
base.a[0][0]=1;
base.a[0][1]=1;
base.a[1][0]=1;
base.a[1][1]=0;
//memset(res.a,0,sizeof(res.a));
for(int i=0;i<2;i++)
{
res.a[i][i]=1;//先初始化res矩阵为二维单位阵,类似于求乘积时初始化res为1
}
while(n)//快速幂
{
if(n&1)
{
res=multiple(res,base);
}
base=multiple(base,base);
n=n>>1;
}
return (((res.a[0][0]*b)%mod+((res.a[0][1]*a)%mod))%mod);
}
int main()
{
int t;
cin>>t;
while(t--)
{
cin>>a>>b>>n>>m;
mod=(int)pow(10,m);
if(n!=0)
{
ll result = quick_power(n-1);
cout << result << endl;
}
else
cout<<a<<endl;
}
return 0;
}
-----------------------
更新:以10为底数的快速幂,应对指数很大很大的情况
函数里的变量依次为底数 指数 模数
ll tenth_pow(ll a,string c,ll mod)
{
ll t=c.size();t--;
ll ans=1,s=a;
while(t>=0)
{
ll cnt=c[t]-'0',cur=s;
for(int i=1;i<=cnt;i++)
{
ans=ans*s%mod;
}
for(int i=1;i<10;i++)
{
cur=cur*s%mod;
}
s=cur;
ans%=mod;
t--;
}
return ans;
}