杭电1061
先上题目:
Rightmost Digit
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 31556 Accepted Submission(s): 12068
Problem Description
Given a positive integer N, you should output the most right digit of N^N.
Input
The input contains several test cases. The first line of the input is a single integer T which is the number of test cases. T test cases follow.
Each test case contains a single positive integer N(1<=N<=1,000,000,000).
Each test case contains a single positive integer N(1<=N<=1,000,000,000).
Output
For each test case, you should output the rightmost digit of N^N.
Sample Input
2 3 4
Sample Output
7 6HintIn the first case, 3 * 3 * 3 = 27, so the rightmost digit is 7. In the second case, 4 * 4 * 4 * 4 = 256, so the rightmost digit is 6.
Author
Ignatius.L
Recommend
解题思路:
三种方法
1.直接计算(如果你有足够的时间的话!)
代码如下:
scanf("%d",&n);
pr=1;
for(i=1;i<=n;i++){
pr=(pr*(n%10))%10;
}
printf("%d\n",pr);
pr=1;
for(i=1;i<=n;i++){
pr=(pr*(n%10))%10;
}
printf("%d\n",pr);
当然,这在杭电上会超时!因为n足够大的时候,循环次数会较多!不妨可以试试哦!
2.就像求素数那样,事先打一个余数表,a[ i ] 存的都是 i^i 的余数,
代码如下:
int a[100000010];
void solve(){
int i,j;
memset(a,0,sizeof(a));
a[1]=1;
for(i=2;i<1000000010;i++){
a[i]=(a[i-1]*(i%10))%10;
}
}
void solve(){
int i,j;
memset(a,0,sizeof(a));
a[1]=1;
for(i=2;i<1000000010;i++){
a[i]=(a[i-1]*(i%10))%10;
}
}
这样的确可以,(对于测试数据较小的时候),此时定义的a[100000010] 的程序能够运行,但杭电上的数据正好,又多了一个0;
那好,我们就加上一个0; 运行时,出现了问题,说定义的数组太大,,,,怎么办呢,,,,回忆一下,我们定义的是整型的数组a[1000000010],,,整形四个字节,,并且,根据需要,我们在a数组中只是存放0~10之间的数,那么我们就没有必要用整形,我们完全可以用字符型数组代替,那好,我们就改一下,,,,,,
如下:
char a[1000000010];
void solve(){
int i,j;
memset(a,0,sizeof(a));
a[1]=1;
for(i=2;i<1000000010;i++){
a[i]=(a[i-1]*(i%10))%10;
}
}
void solve(){
int i,j;
memset(a,0,sizeof(a));
a[1]=1;
for(i=2;i<1000000010;i++){
a[i]=(a[i-1]*(i%10))%10;
}
}
运行,,!哎呀,这次果然没报错,,那好,我们就提交一下,,,提交得到的是Memory Limit Exceeded,超内存了,,看一下程序内存,70多兆,那必须超啊!所以本题用打余数表的方法不是超时,就是超内存,所以这种打表方法在本题是行不通的,我们还要另寻他法,,接下来,就有了3
3.简化运算步骤
由上两种方法可知,打余数表,内存太大,,直接运算呢,超时,,,等等,既然运算超时,那一定是运算的时候,运算次数太多了,那么我们是否可以简化一下运算步骤呢,,当然可以,
根据 剩余定理可知:
(n^m)%10=((n%10)^m)%10
然而,先进行求余,是减小了n,,在一定成度上减少了运算次数,关键是把m也减小,这样才能最大程度的较少运算次数,为此,我们可以进行降幂处理
如下:
m为偶数: n^m=(n*n)^(m/2)
m为奇数时:n^m=n*(n*n)^(m/2)
根据剩余定理:得 m为偶数: (n^m)%10=(((n*n)%10)^(m/2))%10
m为奇数时:n^m=n*(n*n)^(m/2) 同样是这样
由此可得代码如下:
int solve(int n,int m){
if(m==0) return 1;
if(m==1) return n;
if(m%2==0) return solve(n*n%10,m/2);
if(m%2!=0) return n*solve(n*n%10,(m-1)>>1)%10;
}
if(m==0) return 1;
if(m==1) return n;
if(m%2==0) return solve(n*n%10,m/2);
if(m%2!=0) return n*solve(n*n%10,(m-1)>>1)%10;
}
这也是一个较常用的代码片段,完整代码如下:
# include<stdio.h>
int solve(int n,int m){
if(m==0) return 1;
if(m==1) return n;
if(m%2==0) return solve(n*n%10,m/2);
if(m%2!=0) return n*solve(n*n%10,(m-1)>>1)%10;
}
int main(){
int n;
int m,i;
scanf("%d",&n);
while(n--){
scanf("%d",&m);
printf("%d\n",solve(m%10,m));
}
return 0;
}
4,,??这应该也算得上一种方法:
将1到40 的n^n运算打印出来,可以惊奇的发现,数是以20为周期,循环的,,那么,我们就又能做点"手脚"了 嘿嘿!!
如下:
#include<iostream>
using namespace std;
int main()
{
int t,a[21]={0,1,4,7,6,5,6,3,6,9,0,1,6,3,6,5,6,7,4,9,0};
__int64 n;
cin>>t;
while(t--)
{
cin>>n;
cout<<a[n%20]<<endl;
}
return 0;
}
解题时间:2014~07~28
解题人:李富昌
仅供参考!