WEEK8 csp模拟 C - 咕咕东的奇妙序列

在这里插入图片描述

题解

这是一道类似于前缀和的题,但是由于数据范围太大,我们不可能对前缀和进行预处理存到数组中,只能在用到某项的前缀和时临时计算。
我们可以把这个奇妙序列看做许多部分,112123123412345123456…(第n部分是数字1~n的所有字符的排列)当我们要求第n项的字符时,先求它位于这个奇妙序列的哪一部分,因为数据很大,我们用二分法来求。
求出第n个字符位于哪一部分之后,用n减去前面部分的字符之和,就变成了求这一部分的第n个字符是什么。这一部分也有可能很长,所以我们也用了二分法来求。
求出第n个字符位于这一部分的第几个数字之后,用n减去这一部分前面的字符之和,就变成了求这一个数字的第n个字符是什么。我们直接使用to_string函数,求出第n个字符。
两次使用二分法找出前缀和小于n的最后一个点。第一次计算前缀和时(计算前面所有部分的字符数目),因为前面的部分类似于多个等差数列,我们用了等差数列的方法来求(实际比等差数列复杂)。
最后,csp模拟结束后又想到,如果数据太大又时间紧迫临时想不到好的处理方法的话,可以用简单的做法写一下,说不定可以过前几个数据小的点。

代码

#include <iostream>
#include <string>
using namespace std;
int q; long long k;
long long block(long long x) {
    long long  d = 1, i = 1, n = 0, j = 10, ret = 0;
    for (; j <= x; i++, j *= 10) {
        n = j - j / 10;
        ret += d * n + n * (n - 1) * i / 2;
        d = d + (n - 1) * i + (i + 1);
    }
    long long tmp = x - j / 10 + 1;
    return ret + d * tmp + tmp * (tmp - 1) * i / 2;
}
long long numbe(long long x) {
    long long  i = 1, n = 0, j = 10, ret = 0;
    for (; j <= x; i++, j *= 10) {
        n = j - j / 10;
        ret += n * i;
    }
    return ret + (x - j / 10 + 1) * i;
}
int main() {
    cin >> q;
    for (int i = 0; i < q; ++i) {
        //cout << "jinlaile" << endl;
        cin >> k;
        long long l = 0, r = 1000000000;
        long long ans = 0;
        while (l <= r) {
            long long mid = (l + r) >> 1;
            if (block(mid) < k) {
                l = mid + 1;
                ans = mid;
            }
            else
                r = mid - 1;
        }
        k = k - block(ans);
        l = 0, r = 1000000000;
        long long answ = 0;
        while (l <= r) {
            long long mid = (l + r) >> 1;
            if (numbe(mid) < k) {
                l = mid + 1;
                answ = mid;
            }
            else
                r = mid - 1;
        }
        k = k - numbe(answ);
        string str = to_string(answ + 1);
        cout << str[(int)k - 1] << endl;
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值