#include <iostream>
#include <fstream>
using namespace std;
ifstream fin("rmq.in");
ofstream fout("rmq.out");
/// => Arbori de Intervale (Segment Tree) -> in fiecare nod retin minimul fiilor (frunzele iau valorile elementelor vectorului dat) => in varf voi avea minul
// dimensiunea de siguranta este 4*n => 4*100 000
// pt nodul i, fii sunt 2*i+1 si 2*i+2
int stree[400000], v[100000];
void constructST(int i, int st, int dr) {
if (st == dr) {
stree[i] = v[dr];
return;
}
int mij = (st + dr) / 2;
constructST (2*i+1, st, mij ); // fiul stang
constructST (2*i+2, mij + 1, dr ); // fiul drept
stree[i] = min( stree[2*i+1], stree[2 * i + 2] ); // in nod se alege minimul dintre cei 2 fii
}
int RMQ(int i, int st, int dr, int x, int y)
{
// If segment of this node is a part of the given range, then return the min of the segment
if (x <= st && y >= dr)
return stree[i];
// If a part of this segment overlaps with the given range
int mij = st + (dr - st) / 2;
/*// If segment of this node is outside the given range
if (dr < x || st > y)
return 100005; // cea mai mare val posibila, sa ma asigur ca nu va fi considerat nod minim */
if (x > mij)
return RMQ(2 * i + 2, mij + 1, dr, x, y);
else if (y <= mij)
return RMQ(2 * i + 1, st, mij, x, y);
return min( RMQ(2*i+1, st, mij, x, mij), RMQ(2*i+2, mij+1, dr, mij+1, y) );
}
int main()
{
int n, m, x, y;
fin >> n >> m;
// in exemplu, indexarea se face de la 1 la n... => si arborele il voi indexa tot incepand de la 1
for (int i=1; i<=n; i++)
fin >> v[i];
constructST(1, 1, n); // construiesc arborele de intervale incepand cu nodul 1, pentru intervalul [1,n]
for (int i = 0; i < m; i++) { // citesc cele m intervale si afisez minimul pt fiecare
fin >> x >> y;
fout << RMQ(1, 1, n, x, y) << '\n';
}
return 0;
}
// ajutor la implementare: https://www.geeksforgeeks.org/segment-tree-set-1-range-minimum-query/