一、题目描述
二、思路概述
- 将每一个以1开头的为一组(纯数字1,而10、11这样的不算)
- get_sum2(k)函数返回的是k所在块的长度
- get_sum1(k)函数返回的是k所在块的前n块的长度(前缀和)
- 先用二分法求k属于哪一块,再用二分求k属于哪一个数字。并判断应该输出数字的哪一位。
三、细节
- 二分法是复杂度比较低的一种查找方法,可在多处使用
- get_sum1(k)和get_sum2(k)两个函数计算块数和前缀和,更多地是用到数学分析。
四、完整代码
#include<iostream>
#include<math.h>
using namespace std;
//认为每一个以1开头的为一组(纯数字1,10、11这样的不算)
long long get_sum1(long long k)
{//求x所在块的前n块的长度(前缀和)
long long i = 1;
long long j = 1;
long long ans = 0;
for (; 10 * j <= k; i++, j =j* 10)
{
ans = ans + i * ((1 + 9 * j) * 9 * j / 2) + i * j * 9 * (k - j * 10 + 1);
}
return ans + i * (k - j + 2) * (k - j + 1) / 2;
}
long long get_sum2(long long k)
{//求x所在块的长度
long long i = 1;
long long j = 1;
long long ans = 0;
for (; 10 * j <= k; i++, j =j* 10)
{
ans =ans+ i * 9 * j;
}
return ans + i * (k - j + 1);
}
void answer(long long k)
{
//先求k属于哪一块
long long l = 0;
long long r = 1000000000;
long long pre = 0;
while (l < r - 1)
{
long long mid = (l + r) / 2;
if (get_sum1(mid) < k)
{
l = mid;
pre = get_sum1(mid);
}
else r = mid;
}
//求x属于哪个数
k = k - pre;//在目前的块里面,排第k位
r = l + 1;
l = 0;
while (l < r - 1)
{
long long mid = (l + r) / 2;
if (get_sum2(mid) < k)l = mid;//l最后就是哪个数
else r = mid;
}
k = k - get_sum2(l++);//这个数有几位
int tol = 0;
long long ans[50];
while (l)
{
ans[tol++] = l % 10;
l = l / 10;
}
cout << ans[tol - k] << endl;
}
int main()
{
int q;
cin >> q;
while (q--)
{
long long k;
cin >> k;
answer(k);
}
}