P2022 有趣的数

//P2022 有趣的数
//此文写得不错,代码够短,http://www.cnblogs.com/shenben/p/5736624.html摘抄如下:
//由于答案可能非常大,所以这道题显然不能用枚举,即便用二分,时间复杂度{O[N(logN)^2]}也特别大。
//我们可以设所有字典序比K小的数中的第M-1个为X,N就等于K与X的最大值,怎么求X呢?
//当然是枚举。我们把所有字典序比K小的数分成无穷大个集合。
//集合Ai里的任意两个数j,k,都满足i=floor(log10(j))=floor(log10(k))(floor(a)表示取a的整数部分,
//log10(a)表示以10为底,以a为真的对数值),其中最大值为ai。我们可以发现,
//设K的左数第i位是pi,qi=∑pj*(i-j+1)(1<=j<=i),当j<=log10(K),|Aj|=qj-1,aj=qj-1;
//当j>log10(K),|Aj|=|A(j-1)|*10(请读者自己证明)。由此我们可求出X所在集合Ai,
//且X=ai+[M-∑|Aj|(1<=j<=i)]-1。求X所在集合的时间复杂度和求出X所在集合后求X的值的时间复杂度均为O[log10(N)],
//总的时间复杂度为O[log10(N)]。
//搜索了一通,找了个能看得懂的,http://www.cnblogs.com/grhyxzc/p/5742694.html摘抄如下:
//对于该题来说,我们只需考虑比K小的数就可以了,比K小的自然数中,比K小的字典序的个数=K-1。
//eg:
//对于456而言,从100~455 都可以,有456-100-1个。(更正456-100=356 mrcrack)
//从10~45 也可以 有(45-10-1) +1 //45是可以的,以为456还有后面的数,所以45也小于456(字典序)(更正45-10+1 =36 mrcrack)
//从1~4中也都可以,有(4-1-1)+1//原因同上 (更正4-1+1=4 mrcrack)
//(补充,故456位置为397 测试数据 输入:456 397 输出:456 mrcrack)
//由以上,我们便可以找出规律:比K字典序小的数等于ans=(K%10-1)//直到K=0;ans+=(t-1),因为除了位数与原数相同的的情况,等于是成立的,见以上标红部分。
//规律找到,然后逐渐扩大N,以K的10^i扩大,当ans>m时,ans=(k*10^i-(ans-(M-1)+1))//减出多余的部分。
//输入:456 397
//输出:456
//输入:456 397
//输出:1000
//1-45
//10-45
//100-456
//1000-(4560-1) 4560-1-1000+1=3560
//10000-(45600-1) 45600-1-10000=35600
//以 456 398 为例cnt=3560+396=3956 M-1=397 cnt-(M-1)=35599 (4560-1)-35599=1000   (4560-1)-(cnt-(M-1)) cnt-(M-1)多出的个数 由更高层完成故 通式为 (c-1)-(cnt-(M-1))
//按上述思路编写,提交,测试点9,14,16,34,48TLE ,进行1的修改,提交AC 2017-7-26 22:35
//程序特点:纯C语言编写,可以C或C++提交。 目标:大家花时间尽量能看懂的代码(跟踪程序,更容易看懂代码) 。
//题解:该程序的编写,建立在3个样例上(m-1指的是m的前一位),提供给大家:
//样例1
//输入:456 397
//输出:456
//样例2
//输入:456 398
//输出:1000
//样例3
//输入:10 10
//输出:0
//样例1:
//对于456而言,从100~455 都可以,有456-100=356个。
//从10~45 也可以 有45-10+1 =36个 //45是可以的,以为456还有后面的数,所以45也小于456(字典序)
//从1~4中也都可以,有更正4-1+1=4//原因同上
//最大数到456,比456字典序小的个数为(456-100+1)-1+(45-10+1)+(4-1+1)个,即 (456-100+1)+(45-10+1)+(4-1+1)-1个,即396个,getcount函数因此算法编出
//样例2:
//同样例1思路,但第397个数在小于456里面找不到了,只能从1000~(4560-1)里面进行寻找,可寻找的数目(4560-1)-1000+1=3560个,while(cnt<m-1)里面的内容由此编出
//多算个数cnt-(m-1),如样例2,cnt=396+3560 m-1=398-1 cnt-(m-1)=3560+396-(398-1)=3599。
//多算个数的数字,属 1000~(4560-1)区间,故m-1位置处数字是,(4560-1)-(3560+396-(398-1))=1000, 过m-1位置处数字是(c-1)-(cnt-(m-1))
//在m-1,m位数字间取最大值,即为答案,如样例2,1000 456最大值为1000;样例1 455 456 最大值为456
//样例3的原因是,10最多只能有1字典序比10小,再也找不到满足题意的其他数,字典序比10小了,故要添加 k==base&&cnt<m-1的判断,否则 测试点9,14,16,34,48TLE
#include <stdio.h>
long long base,cnt=0;//如456 base=100
void getcount(long long x){//获得小于x的,且字典序小于x的数的个数
    long long base1=1,x1=x,x2=x;
    while(x1){//数出x的位数,如456 计算得出base1=100
        x1/=10;
        base1*=10;
    }
    base1/=10;//因为多 一次*10,故此处 一次/10
    base=base1;//如456 base=100  
    while(x2){//最大数到456,比456字典序小的个数为(456-100+1)-1+(45-10+1)+(4-1+1)个,即 (456-100+1)+(45-10+1)+(4-1+1)-1个,即396个,getcount函数因此算法编出
        cnt+=x2-base1+1;
        x2/=10;
        base1/=10;
    }
    cnt-=1;//cnt计算小于x,且字典序小于x的数的个数
}
long long max(long long a,long long b){
    return a>b?a:b;
}
int main(){
    long long k,m,c,p;
    scanf("%lld%lld",&k,&m);
    getcount(k);
    if(cnt>m-1||k==base&&cnt<m-1){//1 此处写成 if(cnt>m-1)漏了一种情况 10 10 无输出结果。
        printf("0\n");//无解
        return 0;
    }
    c=k,p=k-base;
    while(cnt<m-1){//计算大于x,且字典序小于x的数的个数。 //同样例1思路,但第397个数在小于456里面找不到了,只能从1000~(4560-1)里面进行寻找,可寻找的数目(4560-1)-1000+1=3560个,while(cnt<m-1)里面的内容由此编出
        c*=10;
        p*=10;
        cnt+=p;
    }
    printf("%lld\n",max(k,(c-1)-(cnt-(m-1))));
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值