Il problema richiede di trovare ripetutamente il minimo su un segmento di un array che non cambia dopo la generazione. Il numero di query arriva fino a 10^7, quindi ogni query deve essere elaborata in . Per fare ciò, utilizziamo una sparse table — una struttura dati statica per il problema RMQ (Range Minimum Query).
Costruzione della tabella. Per ogni posizione e ogni tale che i + 2^j - 1 \le n, la cella mas[i][j] memorizza l'indice dell'elemento minimo sul segmento [i; i + 2^j - 1] di lunghezza 2^j. Per j = 0 il segmento è costituito da un solo elemento, quindi mas[i][0] = i.
Per j > 0, dividiamo il segmento di lunghezza 2^j in due metà di lunghezza 2^{j-1}: [i; i + 2^{j-1} - 1] e [i + 2^{j-1}; i + 2^j - 1]. Gli indici dei minimi sulle due metà sono già stati calcolati — sono mas[i][j - 1] e mas[i + 2^{j-1}][j - 1]:
Il minimo dell'intero segmento è uguale al più piccolo dei due minimi delle metà. Pertanto, mas[i][j] è uguale a quale dei due indici mas[i][j - 1] e mas[i + 2^{j-1}][j - 1] punta all'elemento più piccolo dell'array. La tabella viene riempita in ordine crescente di e contiene O(n \log n) celle.
Risposta a una query. Supponiamo di dover trovare il minimo sul segmento . Sia k = \lfloor \log_2(v - u + 1) \rfloor l'esponente della più grande potenza di due che non supera la lunghezza del segmento. Il segmento di lunghezza 2^k che inizia in e il segmento di lunghezza 2^k che termina in coprono insieme completamente , poiché 2 \cdot 2^k > v - u + 1:
Questi segmenti possono sovrapporsi, ma ciò non ha importanza per il minimo: un elemento contato due volte non cambia il risultato (a differenza, ad esempio, della somma). Pertanto, il minimo su è uguale al più piccolo tra gli elementi a[mas[u][k]] e a[mas[v - 2^k + 1][k]], e una query viene elaborata in .
Gli estremi e delle query generate possono comparire in qualsiasi ordine, quindi prima di eseguire una query li scambiamo se necessario.
Complessità. La costruzione della tabella richiede O(n \log n), e ogni query richiede . La complessità complessiva è O(n \log n + m). Con m \le 10^7, è proprio il tempo di query a rendere possibile rientrare nel limite di tempo.
Esempio
Consideriamo l'esempio: n = 10, m = 8, a_1 = 12345. L'array generato coincide con l'array fornito nell'enunciato del problema. La tabella per esso è mostrata di seguito. Le celle contengono gli indici degli elementi minimi; un trattino indica che il segmento va oltre i limiti dell'array.
\boldsymbol{j \setminus i} | ||||||||||
— | ||||||||||
— | — | — | ||||||||
— | — | — | — | — | — | — |
Ad esempio, mas[3][2] = 5: sul segmento , l'elemento minimo è a_5 = 1565158. Il valore mas[2][3] = 2 si ottiene da mas[2][2] = 2 e mas[6][2] = 8, poiché a_2 = 305498 < a_8 = 570265.
La prima query: u_1 = 3, v_1 = 9. La lunghezza del segmento è , quindi k = 2. Il segmento è coperto dai segmenti e , per i quali mas[3][2] = 5 e mas[6][2] = 8. Poiché a_8 = 570265 < a_5 = 1565158, la risposta è ans_1 = 570265.
I parametri della seconda query:
Poiché u_2 > v_2, gli estremi vengono scambiati, e viene calcolato il minimo sul segmento .
Tutte le query sono elencate nella tabella:
\boldsymbol{u_i} | \boldsymbol{v_i} | segmento | \boldsymbol{ans_i} | |
L'ultima query ha i parametri u_8 = 5, v_8 = 3, e la sua risposta è ans_8 = 1565158. Questi sono esattamente i valori che vengono stampati. Si noti che vengono stampati i valori originali u_8 e v_8, non gli estremi ordinati del segmento.
Implementazione dell'algoritmo
Gli elementi dell'array (n \le 10^5) sono memorizzati a partire dall'indice . La sparse table è memorizzata nell'array : mas[i][j] è l'indice dell'elemento minimo sul segmento [i; i + 2^j - 1]. Poiché 2^{16} \le 10^5 < 2^{17}, i valori di non superano , quindi LOGMAX = 17 è sufficiente per la seconda dimensione.
#define MAX 100001
#define LOGMAX 17
int a[MAX];
int mas[MAX][LOGMAX];La funzione check scambia i valori di e se u > v, in modo che u \le v. I parametri vengono passati per riferimento.
void check(int &u, int &v)
{
int temp;
if (u > v) temp = u, u = v, v = temp;
}La funzione RMQ_nlogn costruisce la sparse table.
void RMQ_nlogn(void)
{
int i, j;Prima di tutto, mas[i][0] = i per tutti gli .
for (i = 1; i <= n; i++) mas[i][0] = i;Poi, per ogni finché 2^j \le n, e per ogni tale che il segmento [i; i + 2^j - 1] non vada oltre i limiti dell'array, si sceglie tra mas[i][j - 1] e mas[i + 2^{j-1}][j - 1] l'indice dell'elemento più piccolo.
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 funzione q restituisce l'indice dell'elemento minimo sul segmento . Si calcola k = \lfloor \log_2(j - i + 1) \rfloor e si confrontano i minimi di due segmenti di lunghezza 2^k: [i; i + 2^k - 1] e [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 principale del programma. Si leggono , , il primo elemento dell'array a_1 e i parametri della prima query e .
scanf("%d %d",&n,&m);
scanf("%d %d %d",&a[1],&u,&v);Si generano gli elementi a_2, a_3, \ldots, a_n utilizzando la formula dell'enunciato. Il prodotto 23 \cdot a_i non supera 23 \cdot 16714589 \approx 3.8 \cdot 10^8 e rientra nel tipo .
for(i = 2; i <= n; i++)
a[i] = (23 * a[i-1] + 21563) % 16714589;Si costruisce la sparse table .
RMQ_nlogn();Si elaborano le query una per una.
for (i = 1; i <= m; i++)
{Si copiano gli estremi della query corrente in e e, se necessario, li si ordina con la funzione check. I valori e stessi non vengono modificati: da essi vengono calcolati i parametri della query successiva, e sono quelli che vengono stampati nella risposta.
u1 = u; v1 = v; check(u1,v1);Si trova l'elemento minimo sul segmento [u1; v1].
ans = a[q(u1,v1)];Se la query non è l'ultima, si calcolano i parametri della query successiva utilizzando le formule dell'enunciato.
if (i < m)
{
u = ((17*u + 751 + ans + 2*i) % n) + 1;
v = ((13*v + 593 + ans + 5*i) % n) + 1;
}
}Si stampano i parametri dell'ultima query e la sua risposta.
printf("%d %d %d\n",u,v,ans);