POJ_1019_Number Sequence_二分搜索

电工实习终于结束了,考试难的一比。

题意

给出一个序列:
11212312341234512345612345671234567812345678912345678910123456789101112345678910…
给一个序号i,求出这个位置的数字是多少。
序号从1开始。

输入输出

Input
The first line of the input file contains a single integer t (1 ≤ t ≤ 10), the number of test cases, followed by one line for each test case. The line for a test case contains the single integer i (1 ≤ i ≤ 2147483647)

Output
There should be one output line per test case containing the digit located in the position i.

分析

这个题和前几天做的两个题大体思路类似,把这个序列进行几个层次的分割,然后一层一层缩小目标的范围。
对这个题目来说,先找出这个位置处于哪一个从12345…n序列,那么就需要预处理出以任意数字x结尾的递增序列的长度,并搞出前缀和。之后用二分法求出目标递增序列的结尾n。
确定目标位置处于哪一个递增序列后,要确定目标位置处于哪一个位置,最麻烦的地方在于随着数字增大,一个数字所占位数也会增多,因此先确定目标位置处于几位数,同样的方法,预处理不同位数的信息,二分法求出所在数字位数,以及在这些数字里它处于第几位,之后求解就用这个位置除以位数,对位数取余就行了。
错了一些地方调了好久。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define mxn 100010
#define mxm 7
long long d[mxn];
long long c[mxn];
long long num[mxm];
long long tens[]={0,1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000LL,10000000000LL};
long long n;
int calc_len(int in){
    int ret=0;
    while(in){
        ++ret;
        in/=10;
    }
    return ret;
}
void set_d(){
    num[0]=0;
    for(int i=1;i<mxm;++i){
        num[i]=9;
        for(int j=2;j<=i;++j)
            num[i]*=10;
        num[i]*=i;
        num[i]+=num[i-1];
    }
    d[0]=c[0]=0LL;
    for(int i=1;i<mxn;++i)  d[i]=d[i-1]+calc_len(i);
    for(int i=1;i<mxn;++i)  c[i]=c[i-1]+d[i];
}
int main(){
    //freopen("data.txt","w",stdout);
    set_d();
    int cs,CS=9000;
    scanf("%d",&cs);
    while(cs--){
        //n=++CS;
        scanf("%lld",&n);
        long long l=1,r=mxn;
        while(l+1<r){
            long long m=(l+r)>>1;
            if(c[m]<=n) l=m;
            else    r=m;
        }
        long long dist=n-c[l];
        if(!dist){
            printf("%d\n",(int)(l%10));
            continue;
        }
        l=0,r=mxm;
        while(l+1<r){
            long long m=(l+r)>>1;
            if(num[m]<dist) l=m;
            else    r=m;
        }
        dist=dist-num[l];
        int ta=dist/r;
        int tb=dist%r;
        if(!tb){
            printf("%lld\n",(ta-1+tens[r])%10);
            continue;
        }
        //cout<<"ta "<<ta<<endl;
        tb=r-tb;
        long long tem=ta+tens[r];
        for(int i=0;i<tb;++i)   tem/=10;
        printf("%d\n",(int)(tem%10));
    }
    return 0;
}
点赞