板子题:Welcome - Luogu Spilopelia
https://www.luogu.com.cn/problem/P1226要求a^b的值,当a和b很大时,计算a^b的时间复杂度为O(b),我们考虑一种方式,将时间复杂度降为O(log b)。
思路如下:
当要计算5^10时,我们可以将其拆成计算(5^8)*(5^2)的值。
因为5^8=*(5^2)*(5^2)*(5^2)
因此可以以此简化计算
10的二进制为1010, 10=(2^3)*1+(2^2)*0+(2^1)*1+(2^0)*0
此时可将问题转化为求10的二进制数
如果10的二进制中第 i 位(i从0开始,从右往左数)为1
那么说明该位需要参与幂的运算。
代码如下:
#include <iostream>
using namespace std;
typedef long long ll;
ll ans = 1;
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
ll a, b, p;
cin >> a >> b >> p;
ll n = a, b0 = b;
while (b != 0) { //该循环用于计算b的二进制
ll x = b % 2;
if (x) { //如果第i位不为0
ans = (ans * n);
}
n = (n * n); //每次循环:n=n^2,n=n^4,n=n^8,n=n^16,……
b /= 2;
}
cout << a << '^' << b0 << " mod " << p << "=" << ans % p << endl;
return 0;
}
但是这个代码只能通过2个案例
原因是当a和b非常大时,a^b超出了long long 的范围
此时需要用到一个性质: (a*b)%c=( (a%c)*(b%c) ) %c
证明如下:
设x=a%c, 则a=i*c+x i为任意正整
设y=b%c, 则b=j*c+y j为任意正整数
所以a*b=(i*c+x)*(j*c+y) = i*j*c*c+i*y*c+j*x*c+x*y=(i*j*c+i*y+j*x)*c+x*y
设 k=i*j*c+i*y+j*x ,则a*b=k*c+x*y
(a*b)%c=(k*c+x*y)%c=0+(x*y)%c=( (a%c)*(b%c) ) %c
因此 (a^n)%c=( (a%c)^n ) %c
ac代码如下:
#include <iostream>
using namespace std;
typedef long long ll;
ll ans = 1;
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
ll a, b, p;
cin >> a >> b >> p;
ll n = a, b0 = b;
while (b != 0) {
ll x = b % 2;
if (x) {
ans = (ans * n)%p ; //这样就可以保证不溢出
}
n = (n * n)%p; //这样就可以保证不溢出
b /= 2;
}
cout << a << '^' << b0 << " mod " << p << "=" << ans % p << endl;
return 0;
}
1万+

被折叠的 条评论
为什么被折叠?



