题目概览
最朴素的做法就是for
b
b
b次,每次乘上
a
a
a。
#include <iostream>
#include <cstdio>
using namespace std;
int main()
{
long long a,b,p;
long long ans=a;
--b;
scanf("%lld%lld%lld",&a,&b,&p);
for (int i=1;i<=b;i++)
{
ans*=a;
ans%=p;
}
return 0;
}
但这显然不能过此题,数据范围是1e9
的级别,整体是
1
e
9
1
e
9
m
o
d
p
1e9^{1e9} mod p
1e91e9modp(long long
爆炸)。
虽然每次都 m o d mod mod p p p,但还是会爆,高精度时间复杂度太高,无法过。
那么有没有什么优化呢?答案当然是有的。一般快速幂有两种:
分治法
二进制拆分法
本篇介绍二进制拆分法。
大家都知道,求二进制就是不断
vector <int> ans;
int a;
scanf("%d",&a);
while (a)
{
ans.push_back(a%2);
a/=2;
}
reverse(ans.begin(),ans.end());
这个算法的时间复杂度是
O
(
l
o
g
n
)
O(log n)
O(logn)级的。它在于把大任务分解为小任务。由于
n
n
n有
log
2
n
+
1
\log_{2} n +1
log2n+1个二进制位,那么我们只需要知道
a
1
a^{1}
a1,
a
2
a^{2}
a2,
a
4
a^{4}
a4,…,
a
2
log
n
a^{2^{\log n}}
a2logn后,就可以通过
log
n
\log n
logn次运算解决这个问题。这个序列也很简单:前一个数是后一个数的平方。因此,一个while
就可以解决问题。
实现
#include <iostream>
#include <algorithm>
#include <string>
#include <cstdio>
using namespace std;
long long f(long long a,long long b,long long p)
{
if (p==1)
{
return 0;
}
long long res=1;
while (b)
{
if (b%2==1)
{
res*=a;
res%=p;
}
a=a*a%p;
b/=2;
}
return res;
}
int main()
{
long long a,b,p;
scanf("%lld%lld%lld",&a,&b,&p);
printf("%lld",f(a,b,p));
return 0;
}