解题思路:
首先要找这个序列的规律,从整体来看,规律就是每次都是1到i,i++,观察可以对i进行划分,由i的位数对序列进行划分,称之为大组,比如i为1位数就在一大组,i为2位数就在二大组,具体到i就是小组,比如定位i为12,在二大组的第三小组,可以知道,大组的小组间包含的位数构成等差数列,如i=10,1到10就是11位数,i=11,只是增加了一个两位数(这是二大组的共性),位数为13。我是先通过等差数列和的运算存储每个大组的边界,这样就可以对输入的数进行判断属于哪个大组,再在组内进行二分搜索,判断在哪个小组,最后在该组进行搜索定位到具体的i即可,注意,输入的查询数q在此期间要一直更新,即相对与已知位置的偏移量,这样,定位到i就能查找到具体的某一位是什么了。
**>总结:这里出过很多错误,但是不大记得错误是什么了,emm,注意一下浮点精度这里的问题叭。
实验代码:
#include<iostream>
#include<cstdio>
#include<math.h>
using namespace std;
typedef unsigned long long ull;
long long int a[30];
long long int a1[30];
int tot;
void getAll() {
a1[1] = 1;
ull n = 9;
int i = 1;
for (; a[i - 1] < 1e18; i++) {
a[i] = a[i - 1] + a1[i] * n + (n * (n - 1)) / 2 * i;
a1[i + 1] = a1[i] + (n - 1) * i + i + 1;
n *= 10;
}
tot = i - 1;
for (int i = 1; i <= tot; i++)
cout << a1[i] << endl;
for (int i = 1; i <= tot; i++)
cout << a[i] << endl;
}
int cute(int num, int ans) {
int temp = num;
while (ans < 0) {
temp /= 10;
ans++;
if (ans == 0)
break;
}
return temp % 10;
}
ull get_num(ull a1, ull d, ull ans) {
ull l = 0, r = 9*pow(10,d-1);
while (l < r) {
ull mid = (l + r) >> 1;
if (mid * a1 + mid * (mid - 1) / 2 * d >= ans)r = mid;
else l = mid + 1;
}
return l-1;
}
void getAns(ull k) {
int d;
for (int i = 1; i <= tot; i++) {
if (k <= a[i]) {
d = i;
break;
}
}
long long int ans = k - a[d - 1];
ull a0 = a1[d];
ull n = get_num(a0, d, ans);
ans = ans - (n * a0 + n * (n - 1) * d / 2);
ull temp = pow(10, d - 1) - 1 + n;
int tmp=0;
ull t = 9;
int j = 1;
for (; j <= d; j++) {
ans -= j * t;
if (ans <= 0) {
ans += j * t;
break;
}
tmp+=t;
t *= 10;
}
int mo = ceil((long double)ans / j);
tmp+=mo;
ans = ans - mo*j;
cout << cute(tmp, ans);
}
int main(void) {
getAll();
int n;
ull k;
scanf("%d", &n);
for (int i = 0; i < n; i++) {
cin >> k;
getAns(k);
}
return 0;
}