先来个非正解(乱搞)做法
double 看似不能装下 1 0 1 0 9 10^{10^9} 10109,但是 double 在装不下大数的时候会把自己变成 +inf,此时为了方便处理溢出,此变量将大于所有除了 +inf 的数,是不是很方便!
pow 的时间复杂度为 O ( log n ) O(\log n) O(logn),最大运算量约为 64 次,因此可以通过此题。
上代码!
//
// main.cpp
// P8813 [CSP-J 2022] 乘方(民间数据)
//
// Created by SkyWave Sun on 2022/11/6.
//
#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;
int main(int argc, const char * argv[]) {
int a,b;
scanf("%d%d",&a,&b);
double sum = pow(a, b);
if (sum > 1e9) {
printf("-1\n");
}else {
printf("%d\n",(int)sum);
}
return 0;
}
注意,不能将 s u m sum sum 转成 long long,会溢出,必须要用 double 进行判断再强转输出。
接下来就是中规中矩的做法
先讲思路:这道题的难点就是在于判断溢出,而如果用使用倍增思想的快速幂解决此题,判断边界条件将十分麻烦。
举一个例子,如果要算
9999999
9
65
99999999^{65}
9999999965,快速幂的
a
n
s
ans
ans 一开始会等于
99999999
99999999
99999999,离大于
1
0
9
10^9
109 只有一步之遥,而
b
a
s
e
base
base 快速自举到
9999999
9
2
6
99999999^{2^6}
9999999926,最后 ans *= base
时溢出(属实有点坑队友)。
所以只在每次乘的时候判断 a n s ans ans 是否溢出是不够的,如果判断 base 是否溢出呢?
再看这样一个例子:我要算 9999999 9 1 99999999^1 999999991,显然的,我们的答案小朋友是 99999999 99999999 99999999,没有超过 1 0 9 10^9 109,但是这时候我们的猪队友——base 自举,从 1 0 9 10^9 109 变成了 9999999 9 2 99999999^2 999999992,超过 1 0 9 10^9 109,冤枉了 99999999 99999999 99999999 这个无辜的小朋友。
此时我们在鄙视链最底端的暴力幂小朋友,终于有他的用武之地了——判断溢出十分方便。
我们直接在每次往 a n s ans ans 里乘一次的时候,都判断一下是否超过了 1 0 9 10^9 109,如果超过了就直接退出程序。由于上一次没有退出程序的 a n s ans ans 一定小于 1 0 9 10^9 109,所以再乘一个小于等于 1 0 9 10^9 109 的数也不会因为超出 long long 范围(18位)。
那时间复杂度怎么办? b b b 可是 有 1 0 9 10^9 109 规模,暴力幂岂不是肯定超时?暴力幂小朋友你先别急着哭,让我们再来分析一下。
最劣情况下是 a = b = 1 0 9 a = b = 10^9 a=b=109 吗?其实不然,因为乘第二次的时候就已经超过 1 0 9 10^9 109,后面直接不用判断了,所以我们要找的 a a a 的数值应该保证 b b b 乘满 1 0 9 10^9 109次,所以 a a a 就只能是 1 了。但 1 b 1^b 1b 的 b 在本题的数据范围中无论是几,答案都只能是 1,所以 a = 1 a = 1 a=1 的情况特判掉就行啦。
那每种情况都需要特判吗?其实不然。我们来看 2, 2 27 2^{27} 227 是第一个大于 1 0 9 10^9 109 的数,也就是说最多只要运算 27 次,就会因为不符合题目条件而退出程序。更何况底数比 2 大的情况,最坏情况运算次数肯定更少。
所以我们如果把 a = 1 a = 1 a=1 的情况判掉,时间复杂度就一定是正确的啦,而且跑的飞快:时间复杂度 O ( 1 ) O(1) O(1)!
暴力幂小朋友破涕为笑!
边放代码边讲解:
//
// main.cpp
// test
//
// Created by SkyWave Sun on 2022/11/6.
//
#include <iostream>
#include <algorithm>
using namespace std;
int main(int argc, const char * argv[]) {
unsigned long long a,b;//unsigned 保险一点啦
scanf("%llu%llu",&a,&b);
unsigned long long sum = 1;
if (a == 1) {
printf("%d\n",1);//特判 a = 1
return 0;
}else {
for (int i = 1; i<=b; ++i) {//暴力幂
if (sum > (unsigned long long)1e9) {//不符合题目条件
printf("%d\n",-1);
return 0;
}
sum *= a;//暴力乘
}
}
if (sum > (unsigned long long)1e9) {//最后一次操作也可能超出范围,别忘了再判一次
printf("%d\n",-1);
return 0;
}
printf("%llu\n",sum);
return 0;
}
完结撒花!
我是 SkyWave,这是我的第二篇题解,有不足之处请多多指出,有任何看不懂的地方欢迎留言或者私信!