P8813 [CSP-J 2022] 乘方 题解

先来个非正解(乱搞)做法

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,这是我的第二篇题解,有不足之处请多多指出,有任何看不懂的地方欢迎留言或者私信!

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值