取模意义的快速幂黑科技:龟速乘
一般的快速幂:
ll ksm(ll a,ll n,ll m){
ll ans=1;
while(n){
if(n&1) ans=ans*a%m;
a=a*a%m;
n>>=1;
}
return ans;
}
这样会存在一个问题,当求 a b ( m o d m ) , a^b\pmod{m}, ab(modm),且 a > 1 0 9 a>10^9 a>109时,会存在爆 l o n g l o n g long\ long long long的问题,这时候我们需要对乘法取模进行改正,于是便有了龟速乘。
龟速乘:
为什么要叫龟速乘呢,因为这个乘法运算比计算机底层的乘法速度要慢。
先看代码:
ll qmul(ll x,ll y,ll m){ //龟速乘
ll s=0;
while(y){
if(y&1) s=(s+x)%m;
x=(x+x)%m;
y>>=1;
}
return s;
}
形式与快速幂非常相似,只不过里面的乘法变成加法了,
比如
2
×
5
=
2
+
2
×
4
=
2
+
4
×
2
=
2
+
8
=
10
2\times 5= 2+2\times 4=2+4\times 2=2+8=10
2×5=2+2×4=2+4×2=2+8=10。
时间复杂度: O ( l o g y ) O(logy) O(logy)
快速幂和龟速乘搭配一起就可以欢乐地进行模意义下的快速幂了。
复杂度: O ( l o g n × l o g ( a ) ) O(logn\times log(a)) O(logn×log(a))
ll qmul(ll x,ll y,ll m){ //龟速乘
ll s=0;
while(y){
if(y&1) s=(s+x)%m;
x=(x+x)%m;
y>>=1;
}
return s;
}
///
ll ksm(ll a,ll n,ll m){
ll ans=1;
while(n){
if(n&1) ans=qmul(ans,a,m);
a=qmul(a,a,m);
n>>=1;
}
return ans;
}
例题: U55950 【模板】扩展欧拉定理
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e3+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define fi first
#define se second
#define pb push_back
bool f;
inline void read(ll &s){
int w=1;char c;
while(c=getchar(),!isdigit(c)){
if(c=='-') w=-1;
}
while(isdigit(c)) s=(s<<3)+(s<<1)+(c&15),c=getchar();
s*=w;
}
inline ll readm(ll m){
ll s=0;char c;while(c=getchar(),!isdigit(c)) ;
while(isdigit(c)){
s=(s<<3)+(s<<1)+(c&15);
if(s>=m){
s%=m;
f=true;
}
c=getchar();
}
return s;
}
ll phi(ll n){
ll s=n;
for(ll i=2;i*i<=n;i++){
if(n%i==0){
s-=s/i;
while(n%i==0) n/=i;
}
}
if(n>1) s-=s/n;return s;
}
ll qmul(ll x,ll y,ll m){ //龟速乘
ll s=0;
while(y){
if(y&1) s=(s+x)%m;
x=(x+x)%m;
y>>=1;
}
return s;
}
ll a,m,b;
ll ksm(ll a,ll n,ll m){
ll ans=1;
while(n){
if(n&1) ans=qmul(ans,a,m);
a=qmul(a,a,m);
n>>=1;
}
return ans;
}
int main(){
read(a),read(m);ll phim=phi(m);b=readm(phim);
printf("%lld\n",ksm(a,b+(f?phim:0),m));
return 0;
}