codeforces - 1216E1 Numerical Sequence (easy version) (前缀和/二分)

题目链接:https://vjudge.net/problem/CodeForces-1216E1

  题目描述就是给你一串数,可以分成若干段,每段为这个数字以及这个数字前几个数就像1 12 123 1234 12345这样,然后给你一个数i

让你求这个数列第ai位的数。

  首先我们先想对于1234567891011121314这列数来说,每个数字的末位处于这个数以及这个数前面所有数的位数之和,比如5就位于第

5个位置,10的0位于第11个位置,14的4位于第19个位置,我们可以用一个前缀和来表示这些数的位置。

  然后对于112123123412345这列数来说, 第一个1出现的位置就是1第一个2的位置就是1的位数加上12的位数,第一个11末位1的位置

就是1 + 12 + 123 + 1234 。。。的位数,即上面位数前缀和的前缀和。

  对于一个位置num来说,它前面必定有若干个1 12 123 1234这样的序列,所以我们就可以用二分在前缀和的前缀和序列中找一个小于他

的最大的数,那么他们的差就是序列12345678910...列数中的某个位置,即在前缀和序列中的位置,这里我们可以再用一次二分,然后求得

一个大于或者等于这个数的最小的值,那么num在减去这个位置就是答案了

  

#include<set>
#include<map>
#include<stack>
#include<queue>
#include<cmath>
#include<cstdio>
#include<cctype>
#include<string>
#include<vector>
#include<climits>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define endl '\n'
#define max(a, b) (a > b ? a : b)
#define min(a, b) (a < b ? a : b)
#define mst(a) memset(a, 0, sizeof(a))
#define IOS ios::sync_with_stdio(false)
#define _test printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n")
using namespace std;
typedef long long ll;
typedef pair<int, int> P;
const double pi = acos(-1.0);
const double eps = 1e-7;
const ll MOD = 1e9+7;
const int INF = 0x3f3f3f3f;
const int _NAN = -0x3f3f3f3f;
const int NIL = -1;
const int maxn = 2e5+10;
ll _ls[maxn], _sumls[maxn];
int main(void) {
    for (ll i = 1; i<maxn; ++i) {
        ll t = (ll)((log10(i)+eps)+1); //求数字位数
        _ls[i] = _ls[i-1] + t; //求1234567位数的前缀和(1234567中每个数字末位对应的位数)
        _sumls[i] = _sumls[i-1] + _ls[i]; //求前缀和的前缀和(1121231234中每个数字第一次出现时其末位对应的位数)
    }
    //printf("%lld %lld\n", _ls[maxn-1],_sumls[maxn-1]);
    int n;
    scanf("%d", &n);
    while(n--) {
        ll num;
        scanf("%lld", &num);
        ll *pos1 = lower_bound(_sumls+1, _sumls+maxn, num); 
        if (*pos1 == num) 
            printf("%d\n", (pos1-_sumls)%10);//刚好对应某个第一次出现的数字的末尾
            //指针相减默认是int类型,如果用lld输出结果会出错
        else {
            //对应某个数字的某位上
            --pos1; 
            num -= *pos1;
            ll *pos2 = lower_bound(_ls+1, _ls+maxn, num);
            string nums; //把这个数字转成字符串
            ll t2 = pos2-_ls;
            while(t2) {
                nums += t2%10 + '0';
                t2 /= 10;
            }
            reverse(nums.begin(), nums.end());
            --pos2;
            num -= *pos2; //求出结果在字符串上的位置
            printf("%c\n", nums[num-1]);
            nums.clear();
        }
    }
    return 0;
}

 

posted @ 2020-02-09 20:04  shuitiangong  阅读(230)  评论(0编辑  收藏  举报