El problema requiere encontrar repetidamente el mínimo en un segmento de un arreglo que no cambia después de la generación. El número de consultas alcanza 10^7, por lo que cada consulta debe procesarse en . Para ello, usamos una sparse table — una estructura de datos estática para el problema RMQ (Range Minimum Query, consulta de mínimo en rango).
Construcción de la tabla. Para cada posición y cada tal que i + 2^j - 1 \le n, la celda mas[i][j] almacena el índice del elemento mínimo en el segmento [i; i + 2^j - 1] de longitud 2^j. Para j = 0 el segmento consiste de un solo elemento, por lo que mas[i][0] = i.
Para j > 0, dividimos el segmento de longitud 2^j en dos mitades de longitud 2^{j-1}: [i; i + 2^{j-1} - 1] y [i + 2^{j-1}; i + 2^j - 1]. Los índices de los mínimos en las mitades ya han sido calculados — son mas[i][j - 1] y mas[i + 2^{j-1}][j - 1]:
El mínimo de todo el segmento es igual al menor de los dos mínimos de las mitades. Por lo tanto, mas[i][j] es igual a cualquiera de los índices mas[i][j - 1] y mas[i + 2^{j-1}][j - 1] que apunte al elemento más pequeño del arreglo. La tabla se completa en orden creciente de y contiene O(n \log n) celdas.
Respuesta a una consulta. Supongamos que necesitamos encontrar el mínimo en el segmento . Sea k = \lfloor \log_2(v - u + 1) \rfloor el exponente de la mayor potencia de dos que no excede la longitud del segmento. El segmento de longitud 2^k que comienza en y el segmento de longitud 2^k que termina en juntos cubren completamente , ya que 2 \cdot 2^k > v - u + 1:
Estos segmentos pueden solaparse, pero esto no importa para el mínimo: un elemento contado dos veces no cambia el resultado (a diferencia de, por ejemplo, la suma). Por lo tanto, el mínimo en es igual al menor de los elementos a[mas[u][k]] y a[mas[v - 2^k + 1][k]], y una consulta se procesa en .
Los límites y de las consultas generadas pueden venir en cualquier orden, por lo que antes de ejecutar una consulta los intercambiamos si es necesario.
Complejidad. Construir la tabla toma O(n \log n), y cada consulta toma . La complejidad total es O(n \log n + m). Con m \le 10^7, es precisamente el tiempo de consulta lo que permite ajustarse al límite de tiempo.
Ejemplo
Consideremos el ejemplo: n = 10, m = 8, a_1 = 12345. El arreglo generado coincide con el arreglo dado en el enunciado del problema. La tabla para este arreglo se muestra a continuación. Las celdas contienen los índices de los elementos mínimos; un guion significa que el segmento se sale del arreglo.
\boldsymbol{j \setminus i} | ||||||||||
— | ||||||||||
— | — | — | ||||||||
— | — | — | — | — | — | — |
Por ejemplo, mas[3][2] = 5: en el segmento , el elemento mínimo es a_5 = 1565158. El valor mas[2][3] = 2 se obtiene a partir de mas[2][2] = 2 y mas[6][2] = 8, ya que a_2 = 305498 < a_8 = 570265.
La primera consulta: u_1 = 3, v_1 = 9. La longitud del segmento es , por lo que k = 2. El segmento está cubierto por los segmentos y , para los cuales mas[3][2] = 5 y mas[6][2] = 8. Dado que a_8 = 570265 < a_5 = 1565158, la respuesta es ans_1 = 570265.
Los parámetros de la segunda consulta:
Dado que u_2 > v_2, se intercambian los límites, y se calcula el mínimo en el segmento .
Todas las consultas se listan en la tabla:
\boldsymbol{u_i} | \boldsymbol{v_i} | segmento | \boldsymbol{ans_i} | |
La última consulta tiene los parámetros u_8 = 5, v_8 = 3, y su respuesta es ans_8 = 1565158. Estos son exactamente los valores que se imprimen. Observe que se imprimen los valores originales u_8 y v_8, no los límites ordenados del segmento.
Implementación del algoritmo
Los elementos del arreglo (n \le 10^5) se almacenan comenzando desde el índice . La sparse table se almacena en el arreglo : mas[i][j] es el índice del elemento mínimo en el segmento [i; i + 2^j - 1]. Dado que 2^{16} \le 10^5 < 2^{17}, los valores de no superan , por lo que LOGMAX = 17 es suficiente para la segunda dimensión.
#define MAX 100001
#define LOGMAX 17
int a[MAX];
int mas[MAX][LOGMAX];La función check intercambia los valores de y si u > v, de modo que u \le v. Los parámetros se pasan por referencia.
void check(int &u, int &v)
{
int temp;
if (u > v) temp = u, u = v, v = temp;
}La función RMQ_nlogn construye la sparse table.
void RMQ_nlogn(void)
{
int i, j;Primero, mas[i][0] = i para todo .
for (i = 1; i <= n; i++) mas[i][0] = i;Luego, para cada mientras 2^j \le n, y para cada tal que el segmento [i; i + 2^j - 1] no se salga del arreglo, elegir entre mas[i][j - 1] y mas[i + 2^{j-1}][j - 1] el índice del elemento más pequeño.
for (j = 1; (1 << j) <= n; j++)
for (i = 1; i + (1 << j) - 1 <= n; i++)
if (a[mas[i][j - 1]] < a[mas[i + (1 << (j - 1))][j - 1]])
mas[i][j] = mas[i][j - 1];
else mas[i][j] = mas[i + (1 << (j - 1))][j - 1];
}La función q devuelve el índice del elemento mínimo en el segmento . Calcula k = \lfloor \log_2(j - i + 1) \rfloor y compara los mínimos de dos segmentos de longitud 2^k: [i; i + 2^k - 1] y [j - 2^k + 1; j].
int q(int i, int j)
{
int k = (int)(log(1.0*j - i + 1) / log(2.0));
int res = mas[i][k];
if (a[mas[j - (1<<k) + 1][k]] < a[res])
res = mas[j - (1<<k) + 1][k];
return res;
}La parte principal del programa. Leer , , el primer elemento del arreglo a_1, y los parámetros de la primera consulta y .
scanf("%d %d",&n,&m);
scanf("%d %d %d",&a[1],&u,&v);Generar los elementos a_2, a_3, \ldots, a_n usando la fórmula del enunciado del problema. El producto 23 \cdot a_i no supera 23 \cdot 16714589 \approx 3.8 \cdot 10^8 y cabe en el tipo .
for(i = 2; i <= n; i++)
a[i] = (23 * a[i-1] + 21563) % 16714589;Construir la sparse table .
RMQ_nlogn();Procesar las consultas una por una.
for (i = 1; i <= m; i++)
{Copiar los límites de la consulta actual en y y, si es necesario, ordenarlos con la función check. Los valores y en sí no se modifican: los parámetros de la siguiente consulta se calculan a partir de ellos, y son los que se imprimen en la respuesta.
u1 = u; v1 = v; check(u1,v1);Encontrar el elemento mínimo en el segmento [u1; v1].
ans = a[q(u1,v1)];Si la consulta no es la última, calcular los parámetros de la siguiente consulta usando las fórmulas del enunciado del problema.
if (i < m)
{
u = ((17*u + 751 + ans + 2*i) % n) + 1;
v = ((13*v + 593 + ans + 5*i) % n) + 1;
}
}Imprimir los parámetros de la última consulta y su respuesta.
printf("%d %d %d\n",u,v,ans);