Skip to Content

Digit Queries

Explicación

Nótese que esto es bastante tramposo para un problema de Bronce.

Como kk puede ser tan grande como 101810^{18}, no hay forma de abordar este problema por fuerza bruta. En su lugar, necesitaremos elaborar un algoritmo que pueda responder consultas en tiempo logarítmico.

Agrupemos los números según cuántos dígitos tienen. Una observación que podemos hacer es que para cualquier n1n \geq 1, hay 910n19 \cdot 10^{n-1} números en el grupo con números de longitud nn. Además, para cada grupo, el recuento total de dígitos de todos los números del grupo combinados es nn veces cuántos números hay en el grupo. Por ejemplo:

  • Números de longitud 11: 191\dots 9\rightarrow 99 números 19=9\rightarrow1\cdot9=9 dígitos en total
  • Números de longitud 22: 109910\dots 99\rightarrow 9090 números 290=180\rightarrow2\cdot90=180 dígitos en total
  • Números de longitud 33: 100999100\dots 999\rightarrow 900900 números 3900=2700\rightarrow3\cdot900=2700 dígitos en total
  • Y así sucesivamente…

Para hallar en qué grupo está el número que contiene el kk-ésimo dígito, podemos iterar por los grupos hasta que la suma del número total de dígitos de todos los grupos que hemos procesado sea mayor o igual que kk. En otras palabras, seguimos aumentando el número de grupos que miramos (empezando por los números de 11 dígito) hasta que se cumpla lo siguiente:

n=1# of groupsn910n1k \sum^{\text{\# of groups}}_{n=1}{n\cdot9\cdot10^{n-1}\geq k}

Una vez que la condición se vuelve verdadera, sabemos que el kk-ésimo dígito debe caer en el último grupo usado en la suma. Ahora podemos calcular la posición del kk-ésimo dígito en el grupo restando a kk el número total de dígitos de todos los grupos anteriores. Llamaremos a esta posición relativa jj:

j=k1n=1# of groups1n910n1 j=k-1-\sum^{\text{\# of groups}-1}_{n=1}{n\cdot9\cdot10^{n-1}}

Nótese que restamos 11 del resultado para que jj quede indexado desde 00 (lo que hace más conveniente resolver el resto del problema). Por ejemplo, si j=0j=0, eso significaría que el kk-ésimo dígito es el primer dígito del grupo. Si j=100j=100, eso significaría que el kk-ésimo dígito es el dígito 9999-ésimo del grupo, etc. A partir de ahí, podemos dividir jj por nn (la longitud de los números del grupo), lo que nos da la posición del número que contiene el kk-ésimo dígito dentro del grupo (también indexada desde 00). De forma similar a antes, una posición de 00 significa que el número es el primer número del grupo, y una posición de 100100 significa que el número es el 9999-ésimo número del grupo.

Para calcular el número exacto en el que está el kk-ésimo dígito, podemos sumar la posición del número dentro del grupo (que acabamos de hallar) al valor del primer número del grupo, que es simplemente 10n110^{n-1} (se puede ver este patrón en el ejemplo del principio). Ahora, solo queda hallar la posición del kk-ésimo dígito dentro del número en el que está. Podemos calcularla tomando jmodnj \bmod n, que halla el resto de jj al dividirlo por nn. Por ejemplo, miremos los primeros 1212 dígitos del grupo de números de longitud 33:

Secuencia de dígitos110000110011110022110033
Posición0011223344556677889910101111

Como se puede ver, para cada número de 33 dígitos en la secuencia de dígitos, el primer dígito está siempre en una posición que es múltiplo de 33, lo que significa que la posición módulo 33 es igual a 00. De forma similar, el segundo dígito de cada número está siempre en una posición que es 11 más un múltiplo de 33, lo que significa que su posición módulo 33 es igual a 11. Por último, el tercer dígito de cada número es siempre 22 más un múltiplo de 33, lo que significa que su posición módulo 33 es igual a 22. Este patrón se extiende a números de todos los tamaños, y por eso podemos usar el operador módulo para determinar la posición indexada desde 00 del kk-ésimo dígito dentro del número en el que está.

Ahora, podemos calcular nuestra respuesta convirtiendo el número en el que está el kk-ésimo dígito en un string e indexándolo en la posición que acabamos de hallar.

Implementación

Complejidad temporal: O(qlogk)\mathcal{O}(q\log{k})

#include <bits/stdc++.h> using namespace std; typedef long long ll; // Devuelve 10 elevado a la potencia de "exp" // No usamos pow() porque puede ser inexacto para potencias grandes ll pow10(int exp) { ll product = 1; for (int i = 0; i < exp; i++) { product *= 10; } return product; } int main() { int q; ll k; cin >> q; for (int i = 0; i < q; i++) { cin >> k; /* * Restamos a k los tamaños de los grupos hasta que k sea menor que el tamaño * del grupo actual. Esto produce el mismo efecto que sumar los tamaños de los * grupos hasta que k sea menor o igual que la suma. Para cuando * el bucle while termina, k - 1 será igual a j. */ int n = 1; while (k > n * 9 * pow10(n - 1)) { k -= n * 9 * pow10(n - 1); n++; } // El número exacto en el que está el k-ésimo dígito long num = (k - 1) / n + pow10(n - 1); // La ubicación en num del k-ésimo dígito int loc = (int)((k - 1) % n); // Determinamos la respuesta convirtiendo num a string e indexando en loc cout << to_string(num)[loc] << endl; } }
import java.io.*; import java.util.*; public class DigitQueries { public static void main(String[] args) { Kattio io = new Kattio(); int q = io.nextInt(); for (int i = 0; i < q; i++) { long k = io.nextLong(); /* * Restamos a k los tamaños de los grupos hasta que k sea menor que el * tamaño del grupo actual. Esto produce el mismo efecto que * sumar los tamaños de los grupos hasta que k sea menor o igual que la * suma. Para cuando el bucle while termina, k - 1 será igual a j. */ int n = 1; while (k > n * 9 * pow10(n - 1)) { k -= n * 9 * pow10(n - 1); n++; } // El número exacto en el que está el k-ésimo dígito long num = (k - 1) / n + pow10(n - 1); // La ubicación en num del k-ésimo dígito int loc = (int)((k - 1) % n); // Determinamos la respuesta convirtiendo num a string e indexando en loc io.println(Long.toString(num).charAt(loc)); } io.close(); } // Devuelve 10 elevado a la potencia de "exp" // No usamos Math.pow() porque puede ser inexacto para potencias grandes static long pow10(int exp) { long product = 1; for (int i = 0; i < exp; i++) { product *= 10; } return product; } // CodeSnip{Kattio} }
for _ in range(int(input())): k = int(input()) """ Restamos a k los tamaños de los grupos hasta que k sea menor que el tamaño del grupo actual. Esto produce el mismo efecto que sumar los tamaños de los grupos hasta que k sea menor o igual que la suma. Para cuando el bucle while termina, k - 1 será igual a j. """ n = 1 while k > n * 9 * 10 ** (n - 1): k -= n * 9 * 10 ** (n - 1) n += 1 # El número exacto en el que está el k-ésimo dígito num = (k - 1) // n + 10 ** (n - 1) # La ubicación en num del k-ésimo dígito loc = (k - 1) % n # Determinamos la respuesta convirtiendo num a string e indexando en loc print(str(num)[loc])