题意介绍
一个序列:
112123123412345123456…即第一部分包含1至1之间的所有数字,第二部分包含1至2之间的所有数字,第三部分包含1至3之间的所有数字,第i部分总是包含1至i之间的所有数字。求第k项数字是多少,注意第56项的数字为0.
题意分析
由分析可知:
第一部分到第九部分相邻部分的数字长度加一;
第十部分到第九十九部分相邻部分的数字长度加二;
第一百部分到第九百九十九部分相邻部分数字长度加三;
余下的依次类推。
由此可知,如果把第一部分到第九部分看作是一段,每一段的数字长度组成一个序列,即{1,2,3,4,5,6,7,8,9},则这个序列为一个等差序列,我们可以求出来这一段数字的总长度,往后可以以此类推。等差序列求和公式为 s=a0+n*(n-1)*d/2,其中a0为等差序列的首项,n为序列元素个数,d为公差。
在这道题中,先利用二分找到ans,前ans部分总位数小于或等于k,求出前ans部分总位数,第ans+1部分第(k减去前ans部分总位数)位就为答案。
通过代码
#include<iostream>
using namespace std;
int q;
long long k;
long long getSum(long long a) {
long long end = 1, start = 1, d = 0, n = 0;
long long sum = 0;
while (true) {
end = end * 10;//每一段的结尾数字
d++;
n = end - end / 10;
if (a > end-1) {
sum += start * n + n * (n - 1)*d / 2;
start += (n - 1)*d + d + 1;
}
else {
n = a - end / 10+1;
sum += start * n + n * (n - 1)*d / 2;
break;
}
}
return sum;
}
int solve(long long k) {
int l = 0, r = 1e9, mid = 0, ans;
while (l <= r) {
mid = (l + r) / 2;
long long sum = getSum(mid);
if (sum < k) {
ans = mid;
l = mid + 1;
}
else
r = mid - 1;
}
return ans;
}
void getNum(long long k) {
long long end = 1, d = 0, n = 0, sum = 0,pos=0;
while (k) {
end = end * 10;
n = end - end / 10;
d++;
if (k > n*d) {
sum += n;
k -= n * d;
}
else {
sum += k / d;
pos = k % d;
break;
}
}
if (pos == 0) cout << sum % 10 << endl;
else {
sum += 1;
while (d != pos) {
d--;
sum /= 10;
}
cout << sum % 10 << endl;
}
}
int main() {
cin >> q;
while (q--) {
cin >> k;
int ans = solve(k);
k = k - getSum(ans);
getNum(k);
}
return 0;
}