Digit Queries
Explicación
Nótese que esto es bastante tramposo para un problema de Bronce.
Como puede ser tan grande como , 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 , hay números en el grupo con números de longitud . Además, para cada grupo, el recuento total de dígitos de todos los números del grupo combinados es veces cuántos números hay en el grupo. Por ejemplo:
- Números de longitud : números dígitos en total
- Números de longitud : números dígitos en total
- Números de longitud : números dígitos en total
- Y así sucesivamente…
Para hallar en qué grupo está el número que contiene el -é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 . En otras palabras, seguimos aumentando el número de grupos que miramos (empezando por los números de dígito) hasta que se cumpla lo siguiente:
Una vez que la condición se vuelve verdadera, sabemos que el -ésimo dígito debe caer en el último grupo usado en la suma. Ahora podemos calcular la posición del -ésimo dígito en el grupo restando a el número total de dígitos de todos los grupos anteriores. Llamaremos a esta posición relativa :
Nótese que restamos del resultado para que quede indexado desde (lo que hace más conveniente resolver el resto del problema). Por ejemplo, si , eso significaría que el -ésimo dígito es el primer dígito del grupo. Si , eso significaría que el -ésimo dígito es el dígito -ésimo del grupo, etc. A partir de ahí, podemos dividir por (la longitud de los números del grupo), lo que nos da la posición del número que contiene el -ésimo dígito dentro del grupo (también indexada desde ). De forma similar a antes, una posición de significa que el número es el primer número del grupo, y una posición de significa que el número es el -ésimo número del grupo.
Para calcular el número exacto en el que está el -é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 (se puede ver este patrón en el ejemplo del principio). Ahora, solo queda hallar la posición del -ésimo dígito dentro del número en el que está. Podemos calcularla tomando , que halla el resto de al dividirlo por . Por ejemplo, miremos los primeros dígitos del grupo de números de longitud :
| Secuencia de dígitos | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Posición |
Como se puede ver, para cada número de dígitos en la secuencia de dígitos, el primer dígito está siempre en una posición que es múltiplo de , lo que significa que la posición módulo es igual a . De forma similar, el segundo dígito de cada número está siempre en una posición que es más un múltiplo de , lo que significa que su posición módulo es igual a . Por último, el tercer dígito de cada número es siempre más un múltiplo de , lo que significa que su posición módulo es igual a . 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 del -é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 -ésimo dígito en un string e indexándolo en la posición que acabamos de hallar.
Implementación
Complejidad temporal:
#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])