1019 Number Sequence

1019 Number Sequence

<概要>


S1S2...Sk...という数の集合の列をnumber sequenceという。
Skは12...kという数列である。
入力iが与えられたときi番目の数を答える問題。

!!! 注意 !!!
S10は12345678910であり、11個の数から構成される。
S10の10番目の要素は1であり、11番目の要素は0である。


二分探索×2で解ける。
まず、Sxまでのnumber sequenceの長さを返す関数length(x)を作る。
length(x)は単調増加するので、二分探索によってi番目の数が何個目のgroupに属するかを求められる。
length(l) < i < length(r)みたいな感じ。


例えばSnにi番目の数があるとわかったとする。
するとSnの中のi-length(S_{n-1})番目の数が求める数となる。

次にS∞の中でxまでの数列の長さを返す関数length2(x)を作る。
例えばlength2(12)=len("123456789101112")=15である。
これを使って再び二分探索を行い、i-length(S_{n-1})番目の数を求める。
length2(l) < i-length(S_{n-1}) < length2(r)みたいな感じ。


long long intを初めて使って苦労した。
型指定子は"ll"だ!
scanf("%lld", &i);

#include<cstdio>
#include<cmath>
#include<climits>
#include<string>

using namespace std;

/*
 * x番目のgroupまでの長さを返す
 * たとえばx=3ならば、
 * len("112123")=6を返す
 */
long long int length(long long int x) {
    long long int ret = 0;
    long long int keta = (long long int)log10(x);
    for (long long int i = 0, j = 10; i <= keta; ++i) {
        long long int xx = x - (long long int)pow((double)j, (double)i) + 1;
        ret += xx*(xx+1)/2;
    }
    return ret;
}

/*
 * 無限に続く∞番目のgroupを想定したとき
 * xまでの長さを返す
 * たとえばx=12ならば、
 * len("123456789101112")=15を返す
 */
long long int length2(long long int x) {
    long long int ret = 0;
    long long int keta = (long long int)log10(x);
    for (long long int i = 0, j = 10; i <= keta; ++i) {
        long long int xx = x - (long long int)pow((double)j, (double)i) + 1;
        ret += xx;
    }
    return ret;
}

/* long long int -> string変換
 */
string int2str(long long int num) {
    long long int keta = 1;
    long long int n = (long long int)log10(num);
    string ret = "";
    for (long long int i = 0; i < n; ++i) {
        keta *= 10;
    }
    for ( ; keta >= 1; keta/=10) {
        ret += num / keta + '0';
        num %= keta;
    }
    return ret;
}

int main() {
    int t;
    scanf("%d", &t);
    while (t--) {
	long long int i;
	scanf("%lld", &i);

        // i番目の数字が何番目のgroupに含まれるのか
        // 二分探索で求める
	long long int l = 1, r = 2147483647;
        while (r-l>1) {
	    long long int m = (l+r)/2;
	    if (length(m) > i) {
		r = m;
	    } else {
		l = m;
	    }
	}
	if (length(l) < i) {} // l+1番目のgroupにある
        else { --l;}  // l番目のgroupにある

        // 目的のgroupがわかったら
        // その中で再び二分探索をして、ターゲットを探す
        long long int ii = i-length(l); // l+1番目のgroupのii番目がターゲット
        long long int ll = 1, rr = length(l+1)-length(l);
        while (rr-ll>1) {
            long long int mm = (ll+rr)/2;
            if (length2(mm) > ii) {
                rr = mm;
            } else {
                ll = mm;
            }
        }
        if (length2(ll) < ii) {} // ll+1の中のどれか
        else { --ll; } // llの中のどれか
        
        string strnum = int2str(ll+1); // ll+1を文字列にする
        printf("%c\n", strnum[ii-length2(ll)-1]);
    }
}