题目简介
题目链接
给定一个序列 [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
。
题解
我们可以发现,对于任何的 a
和 b!=0
,(a**b)%10 == (a**(b%4))%10
。
如果
a
的末位是0
, 那么a
的末位一直是0
,1
5
和6
同理。
如果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;
}