K次方(即求n^k的前三位与后三位)

题目来源,多校联合赛: http://acm.hdu.edu.cn/diy/contest_showproblem.php?cid=16820&pid=1002(重开,密码:52acm8)

注意:貌似过了这段时间不能提交了的说。。。。。。

K次方

Time Limit : 2000/1000ms (Java/Other)   Memory Limit : 65535/32768K (Java/Other)
Total Submission(s) : 31   Accepted Submission(s) : 15

Font: Times New Roman | Verdana | Georgia

Font Size:  

Problem Description

所有在程式设计已经有点经验的人都知道,当k很大时你无法完整的表达出n k。例如: C语言的函数 pow(123456,455)能够用double资料型态来表达,但是你却无法得到所有正确的数字。然而,若是能知道一些最左边(leading)和最右边(trailing)数字的话,也可稍微得到一些满足。

Input

输入的第一行有一个整数T(T < 1001),代表有几组测试资料。接下来的T行,每行有2个正整数n和k。n可以用32位元的整数表达,而k<10000001。

Output

每组测试资料输出一行,输出LLL...TTT的样式。其中LLL代表n k的最左边3个数字,TTT代表n k的最右边3个数字。例如123456 2 = 15241383936,所以你应该输出152...936。
你可以假设n k至少有6位数。

Sample Input

3
123456 1
123456 2
2100000056 67333

Sample Output

123...456
152...936
982...016

Author

shenlizhong
感谢kb神的解释:
对于这种大数如果没有像标程一样,转化为log型的,就一定要注意精度问题了。
比赛时用long long 一直错啊,然后取模的精度也出问题了,后来kb神说用double就对了~~~~~~
代码一:
比赛完后,根据标程改的,要注意思路什么的都在里面了
//巧妙的思想,后三位用幂取模可以求出注意补0,
//总位数可利用t=k*log10((double)n)+1 向上取整求出,
//前三位根据方程head=n^k/(10^(t-3))求出
//注意:1求一个数的位数就是把它对10取对数,然后加1即可(科学计数法)
//      2求lnx为log(x);求lgx为log10(x);求以n为底的就是logn();(还好kb神提醒了这一点。。。)
//      3注意重载函数log的调用(强制类型转换,并且加上头文件#include<math.h>):
//       “long double log(long double)”
//       “float log(float)”
//       “double log(double)”
//推荐一道求n的阶乘的位数的题目hdu 1018 (斯特林公式)

//31 MS	276 KB	Visual C++
#include<iostream>
#include<cmath>
#include<cstdio>
using namespace std;
int pow_mod(int a,int n,int m)//就是那个很快的求a^n%m的快速幂的函数,可以去看下我的那个快速幂模板的博客
{
    if(n==0)
       return 1%m;
    int ret=1;
    while(n>0)
    {
        if(n&1)
        {
            ret=(ret*a)%m;
        }
        n>>=1;
        a=(a*a)%m;
    }
    return ret;
}
int main()
{
    int T,n,k;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&k);
        int t=k*log10((double)n)+1;//求n^k的位数,科学计数法思想。。。
        int head,tail;
        tail=pow_mod(n%1000,k,1000);
        for(head=100;log((double)head)<=k*log((double)n)-(t-3)*log((double)10);head++);//注意这里的分号';',区别于我们平时的for()循环,中间的表示head<=n^k/10^(t-3);数学问题毕竟是要写在纸上才看的懂的
        head--;//因为前面的for()中多加了一位,所以这里head减去一位
        printf("%3d...%03d\n",head,tail);
    }
    return 0;
}

代码二:看了kb神的代码后,改的我比赛时错的代码
/*
float的范围为-2^128~+2^128,也即-3.40E+28~+3.40E+38;
double的范围为-2^1024~+2^1024,也即-1.79E+308~+1.79E+308.
*/
//更正的代码(改成double就对了。。。long long桑不起啊):0 MS	260 KB	Visual C++	
#include<stdio.h>
const double mod=1e20;//精度问题桑不起啊!!!
const int m1=1000;
int pow1_mod(__int64 m,int n)//求m^n%1000的快速幂函数,不解释。。。(求后三位)
{
    int b=1;
    m%=m1;
	if(n==1)
		return m;
    while(n>0)
    {
        if(n&1)
        {
            b=(b*m)%m1;
        }
        n=n>>1;
        m=(m*m)%m1;
    }
    return b;
}

int pow2_mod(__int64 m,int n)//截取前面很长的一段(那么后面的数对于前三位的影响就很小了,精度问题永远都是硬伤啊),然后不停的相乘,不停的取模(求前三位)
{
    double b=1;
    double temp=(double)m;
    while(temp>mod)
      temp/=10;
    while(n>0)
    {
        if(n&1)
        {
            b=(b*temp);
            while(b>mod)
              b/=10;
        }
        n=n>>1;
        temp=temp*temp;
        while(temp>mod)
          m=temp/=10;
    }
    while(b>=m1)
     b/=10;
    return (int)b;
}

int main()
{
    int test;
    __int64 n;
    int k;
    int ans1,ans2;
    while(scanf("%d",&test)!=EOF)
    {
        while(test--)
        {
           scanf("%I64d%d",&n,&k);
           ans1=pow2_mod(n,k);
           ans2=pow1_mod(n,k);
           printf("%d...%03d\n",ans1,ans2);
        }
    }
    return 0;
}

代码三:(来自kb神原创,用double秒掉的,无限膜拜强大的kb神啊大笑
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
#include<math.h>
using namespace std;
const double qq=1e20;
int solve1(long long a,int n)
{
    a%=1000;
    int temp=a;
    int ret=1;
    while(n)
    {
        if(n&1)
        {
            ret*=temp;
            ret%=1000;
        }
        temp*=temp;
        temp%=1000;
        n>>=1;
    }
    return ret%1000;
}
int cal2(long long a,int n)
{
    double ret=1;
    double temp=(double)a;
    while(n)
    {
        if(n&1)
        {
            ret*=temp;
            while(ret>=qq)ret/=10;
        }
        temp*=temp;
        while(temp>=qq)temp/=10;
        n>>=1;
    }
    while(ret>=1000)ret/=10;
    return (int)ret;
}

int main()
{
   // freopen("A.in","r",stdin);
   // freopen("A.out","w",stdout);
   // printf("%lf\n",qq);//高精度,桑不起啊
    long long a;
    int n;
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%I64d%d",&a,&n);
       // printf("%d\n",cal2(a,n));
        printf("%d...%03d\n",cal2(a,n),solve1(a,n));
    }
    return 0;
}


代码四:改的标程的代码(坑爹的标程,都不给个能AC的代码)不过还是很膜拜标程的log使用了

//更正后的标程46 MS	276 KB	Visual C++
#include<iostream>
#include<cmath>
#include<cstdio>
using namespace std;
int pow_mod(int a,int n,int m)
{
    if(n==0)
    return 1%m;
    long long x=pow_mod(a,n/2,m);
    x=(x*x)%m;
    if(n%2)
    x=(x*a)%m;
    return (int)x;
}
int main()
{
    int T,n,k;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&k);
        int t=k*log10((double)n)+1,head,tail;
        tail=pow_mod(n%1000,k,1000);
        for(head=100;log((double)head)<=k*log((double)n)-(t-3)*log((double)10);head++);
        head--;
        printf("%3d...%03d\n",head,tail);

    }
    return 0;
}




原文地址:https://www.cnblogs.com/freezhan/p/2776489.html