// Range Minimum Query
/*
n secvente de lungime 1
n-1 secvente de lungime 2
n-3 secvente de lungime 4
n-7 secvente de lungime 8
...
lungime n
=> spatiu de stocare O(n * log n)
retinem MINIMUL din intervalele:
[1, 1], [2, 2], [3, 3], ... , [n, n]
[1, 2], [2, 3], [3, 4], ... , [n-1, n]
[1, 4], [2, 5], [3, 6], ... , [n-3, n]
...
[1, k], [2, k+1], ... , [n-k+1, n]
k - putere a lui 2
2^k -> (1 << k)
rmq[n][log n]
rmq[i][j] - minimul intervalului care incepe pe pozitia i si are lungime 2^j
*/
#include <iostream>
#include <fstream>
using namespace std;
const int mxN = 1e5 + 5, lgN = 17;
int n, m, v[mxN];
int rmq[mxN][lgN];
ifstream fin("rmq.in");
ofstream fout("rmq.out");
// O(log n)
int query(int a, int b) {
// min [a, b] ?
int d = b - a + 1, mini = v[a];
int p = 0;
while (d) {
if (d % 2 == 1) {
mini = min(mini, rmq[a][p]);
a += (1 << p);
}
p++;
d /= 2;
}
return mini;
}
int query_O1(int a, int b) {
}
int main() {
fin >> n >> m;
for (int i = 1; i <= n; i++) {
fin >> v[i];
rmq[i][0] = v[i];
}
// i => i + (1 << j) - 1
// O(n * log n)
for (int j = 1; j < lgN; j++) {
for (int i = 1; i + (1 << j) - 1 <= n; i++) {
// interval care incepe la i de lungime 2^(j-1)
// interval care incepe la i + 2^(j-1) de lungime 2^(j-1)
// => interval care incepe la i de lungime 2^j
rmq[i][j] = min(rmq[i][j - 1], rmq[i + (1 << (j - 1))][j - 1]);
}
}
// O(m * log n)
for (int i = 1; i <= m; i++) {
int a, b;
fin >> a >> b;
fout << query(a, b) << "\n";
}
// Total: O(n * log n + m * log n)
return 0;
}