OpenJudge-49:计算对数

49:计算对数


Description
给定两个正整数a(a>1)和b。可以知道一定存在整数x,使得
x <= logab < x + 1 或者 ax<= b < ax+1
请计算x。

Input
两行,第一行是a,第二行是b。每个整数均不超过100位。
Output
一行,即对应的x。输入数据保证x不大于20。

Sample Input

10000
1000000000001

Sample Output

3

题目解析
这道题有两个考点——大整数乘法二分查找。(不知道对数是什么的同学戳一下 ->百度百科-对数
首先我们需要知道如何用大整数处理对数。用目前知道的方法,用大整数是很难实现的,于是我们必须要换一种方法
作者最先想到的方法是用乘方代替对数。但是这样并不能一次就确定答案,而是要不断向答案靠近——这直接指向了二分查找。我们把答案(ans)设置为不断接近的目标,又因为输入的大整数a,b都是大于1的正整数,因此答案的最小值是0,此时的结果是最小的正整数1,答案的最大值不需要我们确定,题目已经给出,是20。那么我们可以设置两个边界变量right,left,表示当前情况 left<=ans<=right ,在二分查找中不断缩小边界以确定最终答案
先不管乘方计算,我们来看看二分查找。首先是一个mid变量储存中间值,作为乘方计算的指数。计算出乘方后,我们需要改变边界的值。因为我们计算的底数一定是正整数,那么一般来说(除了1)指数越大,则幂越大。我们也可以反过来想,也就是说如果乘方结果大于b,则说明指数过大,相反,则是指数小了一些。换入二分查找则是如果计算结果(Set)比b大,则将右边界(right)改为mid,反之则左边界(left)改为mid
再回过头来看乘方运算。大家都知道这样一个式子:

a^n=a*a*a*...*a*a(n个a)

那么我们可以理解为a乘n次。那么就可以用一个for循环,循环变量(i)从1开始,循环mid次,每次对上一次得出的结果作一次乘法运算。最后的结果就是乘方结果了。别忘了把储存结果的大整数初始化为1!
但是有同学会担心内存问题,其实这种担心是确实存在的。题目要求的大整数最大有100位,而最大的指数是20,则运算结果最大会有10^40位…作者也心存疑虑,BUT…提交了,哈哈哈,AC了。这数据是真心水,大家开10005位的大整数就可以了


题外话
NOI就要到了,老师突发奇想让我们刷1.13里的题,感觉还不错。作者多久没有看前面的题了,发现以前不好做的题现在一天就做了好几道,也算是与时俱进吧。在这里也祝各位向往着NOI的同学能够天天进步,与时俱进,在NOI上绽放光彩!(作者也会去的)


程序样例
1次AC的水数据,二分查找+大整数计算

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct Cheak
{
    int num[10005],len;
    Cheak()
    {
        memset(num,0,sizeof(num));
        len=0;
    }
};
char sa[105],sb[105];
Cheak Algorithm(Cheak Setting1,Cheak Setting2)
{
    Cheak Over;
    for(int i=1;i<=Setting1.len;i++)
    {
        int op=0;
        for(int j=1;j<=Setting2.len;j++)
        {
            Over.num[i+j-1]=Setting1.num[i]*Setting2.num[j]+op+Over.num[i+j-1];
            op=Over.num[i+j-1]/10;
            Over.num[i+j-1]%=10;
        }
        Over.num[i+Setting2.len]=op;
    }
    Over.len=Setting1.len+Setting2.len;
    while(Over.num[Over.len]==0 && Over.len>1)
        Over.len--;
    return Over;
}
int Point(Cheak a,Cheak b)
{
    int maxlen=max(a.len,b.len);
    for(int i=maxlen;i>=1;i--)
    {
        if(a.num[i]>b.num[i])
            return 1;
        if(a.num[i]<b.num[i])
            return -1;
    }
    return 0;
}
int main()
{
    Cheak a,b;
    scanf("%s%s",sa,sb);
    a.len=strlen(sa);b.len=strlen(sb);
    for(int i=1,j=a.len-1;i<=a.len;i++,j--)
        a.num[i]=sa[j]-'0';
    for(int i=1,j=b.len-1;i<=b.len;i++,j--)
        b.num[i]=sb[j]-'0';
    int left=0,right=20;
    while(left+1<right)
    {
        int mid=(left+right)/2;
        Cheak Set;
        Set.num[1]=1;
        Set.len=1;
        for(int i=1;i<=mid;i++)
            Set=Algorithm(a,Set);
        switch(Point(Set,b))
        {
            case 1: right=mid;break;
            case 0: printf("%d\n",mid);return 0;
            case -1: left=mid;break;
        }
    }
    printf("%d\n",left);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值