CSP-M2-补题-C-咕咕东的奇妙序列

题目:

咕咕东的奇妙序列


个人思路:

  • 读了好多遍题才明白了是怎么回事。。。将数字当成字符来求解第k个字符。eg: 数字 100意思是长度为3的字符串100。

  • 在更深入了解之后,我越发觉得这是一道数学题。。通过写数列的变化我们不难看出:

     假设 n 代表第 n 组数据:
     1、n <= 9时,其是一个公差d = 1的等差数列
     2、10 <= n <= 99时,公差d = 2;
     3、100 <= n <=999时,公差d = 3;
     ......
     i、10^i <= n <= 10^i-1,公差 d = i+1;
    
  • 然后,我发现,对于某一项来说,或者这么说吧,将数据的组分类别划分:10i-10i+1为划分规则,处于这中间的组为同一类别,因为他们的求和公式相同。

  • 又因为对于第n向来说,其合又等于前一项的和加上新增的数,类似于:Sn = Sn-1 + an;所以可以用前缀和来求。

  • 剩下的就比较大众化了。因为数据范围的原因,我们不可能对前缀和进行预处理,所以用了二分的方法来找到第k项前的组数。然后第二次二分找到了所在分组的次序。


代码块:

#include <iostream>
#include <string>
#define ll long long
using namespace std;
int q; ll k, pos;
ll get_num(ll x){
    ll  a = 1, d = 1, n = 0, judge = 10, sn = 0;
    for(; judge <= x; d++, judge*=10){
        n = judge - judge/10;//项数
        sn += a*n+n*(n-1)*d/2;//理解为前缀和,a*n+n*(n-1)*d/2 是第n组的和
        a = a +(n-1)*d + (d+1);//下一组的首项
    }
    ll nn = x - judge/10 + 1;//当前组首项到x的项数
    return sn + a*nn+nn*(nn-1)*d/2;
}
ll get_num2(ll x){//找到某一组中x的前面的字符数
    ll  d = 1, n = 0, judge = 10, sn = 0;
    for(; judge <= x; d++, judge*=10){
        n = judge - judge/10;
        sn += n*d;
    }
    return sn + (x - judge/10 + 1)*d;
}
int main() {
    cin >> q;
    for(int i = 0; i < q; ++i){
        cin >> k;
        ll l = 0, r = 1e9;
        int ans = 0;
        while(l <= r){//在K所在组前面有多少组
            ll mid = (l+r) >> 1;
            if(get_num(mid) < k){
                l = mid + 1;
                ans = mid;
            }
            else
                r = mid - 1;
        }
        k = k - get_num(ans);
        l = 0, r = 1e9;//切记r一定要合适,不然的话后果惨重。
        pos = 0;
        while(l <= r){//二分查找k在小组中的位置,返回的是第一个小于k所在数的数。。
            ll mid = (l+r) >> 1;
            if(get_num2(mid) < k){
                l = mid + 1;
                pos = mid;
            }
            else
                r = mid - 1;
        }
        string outs = to_string(pos+1);
        cout << outs[ k - get_num2(pos) - 1] << endl;
    }
    return 0;
}

附:
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值