题解
可以观察出奇妙序列的一些数学规律,如奇妙序列的长度和末尾数
n
n
n的关系,每一个区间
[
1
,
i
]
[1,i]
[1,i]中前
j
j
j个数的长度。故我们分成三步,第一步是求解
k
k
k所在的
[
1
,
i
]
[1,i]
[1,i]区间,之后再在这一段区间里求解具体的数
j
j
j,之后直接取出对应的数字即可。
先求出
[
1
,
i
]
[1,i]
[1,i]的长度规律,可以发现:
1
<
=
i
<
=
9
,
l
e
n
=
i
;
1<=i<=9, \ len=i;
1<=i<=9, len=i;
10
<
=
i
<
=
99
,
l
e
n
=
2
∗
i
−
9
;
10<=i<=99,\ len=2*i-9;
10<=i<=99, len=2∗i−9;
100
<
=
i
<
=
999
,
l
e
n
=
3
∗
i
−
(
99
+
9
)
;
100<=i<=999,\ len=3*i-(99+9);
100<=i<=999, len=3∗i−(99+9);
.
.
.
...
...
1
0
e
<
=
i
<
=
1
0
e
+
1
−
1
,
l
e
n
=
e
∗
i
−
(
1
0
e
+
1
−
1
+
.
.
.
+
99
+
9
)
;
10^e<=i<=10^{e+1}-1,\ len=e*i-(10^{e+1}-1+...+99+9);
10e<=i<=10e+1−1, len=e∗i−(10e+1−1+...+99+9);
其中
e
e
e代表数字
i
i
i长度。
如此我们可以根据
i
i
i求出这个区间的长度以及末尾数字为n的奇妙序列总长
∑
i
=
1
n
l
e
n
i
{\displaystyle \sum _{i=1}^{n}len_{i}}
i=1∑nleni,复杂度是
O
(
e
)
,
e
<
=
18
O(e),e<=18
O(e),e<=18。
那么如何由
k
k
k推导出对应的
i
i
i呢,由于这个规律中,
i
,
i
−
1
i,i-1
i,i−1之间没有明显的权重差异,不能用取模分离出
i
i
i。而求解序列长度的复杂度几近于一个大常数,故可以采用设解验证的二分。找出对应的
i
i
i,再二分找出
j
j
j,具体求法和公式实现见下列程序。
代码
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iomanip>
using namespace std;
#define rep(i,s,t) for(long long i=s;i<=t;i++)
#define mem(a,s) memset(a,s,sizeof(a))
typedef long long ll;
ll sum(ll k){
ll K = k, len = 0;
while(K){
K /= 10;
len++;
}
//从1开始
ll tt = 0,e=0,x=0,s=1;
rep(i,1,len){
e =e * 10 + 9;
if(i==len)
e = k;
tt += (i * (e + s) * (e - s + 1) / 2 - (e - s + 1) * x);
s *= 10;x += e;
}
return tt;
}
ll erfen(ll k){
//<=的第一个位置
ll l=1,r=1e9,ans=0;
while(l<=r){
ll m=(l+r)>>1;
if(sum(m)>=k){
ans=m;
r=m-1;
}else l=m+1;
}
return ans;
}
ll tt(ll x){
ll K = x,tx = 0,len = 0,ttx=0;
while(K){
K /= 10;
len++;
if(K){
tx = tx * 10 + 9;
ttx += tx;
}
}
return len * x - ttx;
}
int make(ll k,ll R){
ll l=0,r=R,ans=0;
while(l<=r){
ll m=(l+r)>>1;
if(tt(m)>=k){
ans=m;
r=m-1;
}else l=m+1;
}
k -= tt(ans - 1);
string s = to_string(ans);
return s[k - 1]-'0';
}
int main(){
// freopen("in.txt","r",stdin);
cin.sync_with_stdio(false);
cout << sum(1e9) << endl;
int Q;
cin >> Q;
while(Q--){
long long k;
cin >> k;
long long f = erfen(k);
cout << make(k-sum(f-1),f)<<endl;
}
return 0;
}