跳至主要内容

Digit Queries

題目

考慮一個按照遞增順序包含所有正整數的無窮字串: 12345678910111213141516171819202122232425...

你的任務是處理 qq 個查詢:第 kk 個字元是什麼數字?

輸入

  • 第一行輸入一個正整數 qq,代表查詢的數量。(1q10001 \le q \le 1000
  • 接著有 qq 行,每行有一個正整數 kk。(1k10181 \le k \le 10^{18}

輸出

輸出 qq 行,各自對應一個查詢。 對每個查詢,輸出字串第 kk 個字元。

範例測資

Input :
3
7
19
12

Output :
7
4
1

解釋如下:

1234567891011121314151617181920...
^ ^ ^ ^
1 7 12 19

想法

我們可以找第 kk 個字元落在哪個正整數 nn 裡面,又是 nn 從高位開始算起的哪一位數,從而找出答案。比如第 266266 個字元落在正整數 125125 裡面,並且是 125125 從高位開始算起的第 22 位數,所以第 266266 個字元是 2

要知道第 266266 個字元落在正整數 125125 裡面,我們可以透過觀察分析:

  • 一位數有 1199,共 99 個數字,佔去了前 99 個字元;
  • 二位數有 10109999,共 9090 個數字,佔去了接下來共 180180 個字元;
  • 三位數有 100100999999,共 900900 個數字,又佔去了接下來共 27002700 個字元。

189<2662889189 < 266 \le 2889,表示第 266266 個字元超過了整個一位數與二位數的區域,落在某個三位數裡面。而又因為 253<266189263,25 \cdot 3 < 266 - 189 \le 26 \cdot 3, 我們知道第 266266 個字元又恰好超過了 2525 個三位數,落在第 2626 個三位數裡,那就是在正整數 125125 裡面。更進一步因為 266189253=2,266 - 189 - 25 \cdot 3 = 2, 我們能知道它是正整數 125125 從高位開始算起的第 22 位數,也就是 2

把這個流程一般化。我們可以發現 \ell 位數的正整數有 10110^{\ell-1}10110^\ell - 1,共 91019 \cdot 10^{\ell-1} 個數字。如果說第 kk 個字元落在某個 \ell 位數裡面,那 \ell 就滿足 (i=119i10i1)<k(i=19i10i1)\Bigl(\displaystyle\sum_{i=1}^{\ell-1} 9i \cdot 10^{i-1}\Bigr) < k \le \Bigl(\sum_{i=1}^\ell 9i \cdot 10^{i-1}\Bigr) 接著找到 ss 使得 s<k(i=119i10i1)(s+1), s\ell < k - \Bigl(\displaystyle\sum_{i=1}^{\ell-1} 9i \cdot 10^{i-1}\Bigr) \le (s+1)\ell, 就代表第 kk 個字元落在第 (s+1)(s+1)\ell 位數裡面,也就是在正整數 n=101+sn = 10^{\ell-1} + s 裡面。更進一步它會是正整數 nn 從高位開始算起的第 k(i=119i10i1)sk - \Bigl(\displaystyle\sum_{i=1}^{\ell-1} 9i \cdot 10^{i-1}\Bigr) - s\ell 位數。

範例程式碼

C++ 範例
#include <iostream>
#define int long long
using namespace std;

signed main() {
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int q, k;
cin >> q;
while (q--) {
cin >> k;
int lv = 1, ten = 1;
while (k >= lv * 9 * ten) {
k -= lv * 9 * ten;
lv++;
ten *= 10;
}
int s = (k-1) / lv;
int n = ten + s;
for (int i = lv; i > k - s*lv; i--) {
n /= 10;
}
cout << n % 10 << '\n';
}
}