【codewars】【题解】【3kyu】Last digit of a huge number

题目简介

题目链接
给定一个序列 [x1, x2, x3, ... xn],请计算 x1^(x2^(x3(...^xn))) 的最后一位,完成下列函数

int last_digit(list<int>array){
	// write your code here
}

例如,对于序列 [3, 4, 2],你需要返回 1 因为 3**(4**2) = 3**16 = 43047621
需要注意:指数运算增长速度非常快。比如,9**(9**9) 有超过 3 亿 6900 万位。
边界情况: 假定 0**0=1,并且空序列的返回值为 1

题解

我们可以发现,对于任何的 ab!=0(a**b)%10 == (a**(b%4))%10

如果 a 的末位是 0, 那么 a 的末位一直是 01 56 同理。
如果 a 的末位是 4,那么 a 的末位一直是 4,6,4,6,... 循环,两次一循环。9 同理
2 3 7 8 的末位四次一循环

所以对于给定序列 [x1, x2, x3, ..., xn],我们可以把问题转化为计算子序列 x2**(x3**(...**xn)))%4 的值。我们先观察 (a**b)%4 乘法有什么规律。

如果 b==0,那么对于任意的 a(a**b)%4==1
如果 a%4==1,那么对于任意的 b(a**b)%4==1
如果 a%4==2,那么如果 b==1,那么 (a**b)%4==2

b==0 的情况我们已经判断过了

如果 a%4==3,那么对于不同的 b

如果 b%2==0,那么(a**b)%4==1
如果 b%2==1,那么(a**b)%4==3

所以我们可以从右往左运算,令已经运算的子序列得到的结果为 b,令未运算的部分最右边为 a。并且我们需要保存这样一个变量 lower,用于保存 b 可能的最小值。

考虑这样的情况,当 a==2 时,如果我们只保存 b%4 的值的话,假设 b%4==1,我们并不知道 b 的值就是 1,还是 b 的值大于 1,只是它对于 4 的模刚好为 1而已

因为我们不能保存 b 的值,因为 b 的值会非常大,可能会有溢出错误,所以只能通过保存 b%4 的值的方式

最后我们使用 x2**(x3**(...**xn)))%4 的值,和 x1%10 的值,获得最终结果。

代码

#include <list>
#include <iostream>
#include <cassert>
#include <vector>

using namespace std;
int last_digit(list<int> array){
    if (array.size() == 0){
        return 1;
    } else if (array.size() == 1){
        return array.front()%10;
    } 
    

    int lower = 0;
    int b = array.back();
    int a;
    array.pop_back();
    lower = b;

    while (array.size() >= 2){
        a = array.back();
        array.pop_back();
        // try to get (a**b)%4
        if (a==0){
            if (lower==0){
                assert(b%4==0);
                b = 1;
                lower=1;
                continue;
            } else {
                b = 0;
                lower = 0;
                continue;
            }
        }
        if (lower==0){
            assert(b==0);
            b = 1;
            lower = 1;
            continue;
        }
        switch (a%4){
        case 0:
            b = 0;
            break;
        case 1:
            b = 1;
            break;
        case 2:
            if (lower == 1){
                b = 2;
            } else {
                b = 0;
            }
            break;
        case 3:
            switch(b%2){
                case 0: b=1; break;
                case 1: b=3; break;
            }
        }
            
        lower = a;
    }

    a = array.back();
    a %= 10;

    if (lower == 0){
        return 1;
    } 
    if (a%10==0){
        return 0;
    }
    if (lower == 1){
        return a%10;
    }

    int ans = 1;
    b %= 4;
    b += 4;
    while (b--) {
        ans *= a;
        ans %= 10;
    }
    
    return ans%10;
}

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
国赛题目一般来源于科学与工程技术、人文与社会科学等领域经过适当简化加工的实际问题。参赛者不需要预先掌握深入的专门知识,只需要学过高等学校的数学基础课程。对于解题思路,可以根据题目要求进行以下步骤来完成建模和求解: 1. 理解题目:仔细阅读题目,理解问题的背景和要求。注意提取关键信息,明确问题的目标和约束条件。 2. 建立数学模型:根据题目的描述和要求,将实际问题转化为数学模型。可以使用已知的数学理论、公式和方法,结合问题的特点进行建模。根据问题类型,可以将问题归类为分类问题、优化问题、预测问题或评价问题。 3. 求解模型:根据建立的数学模型,使用适当的数学工具和方法进行求解。可能需要进行数值计算、优化算法或统计分析等操作,以得到问题的解答。 4. 分析和检验结果:对求解结果进行分析和检验,验证其合理性和正确性。可以通过对比实际数据或进行敏感性分析来评估模型的准确性和可靠性。 5. 模型的改进:根据对结果的分析和检验,对模型进行改进。可以尝试不同的假设或调整参数,以提高模型的性能和适应性。 综上所述,解题思路主要包括理解题目、建立数学模型、求解模型、分析和检验结果以及模型的改进。具体的解题思路会根据不同的题目和问题类型有所差异,建议参赛者根据具体题目要求和自身的数学知识经验,灵活运用数学方法和工具,进行问题的建模和求解。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值