#include <iostream>
#include <vector>
#define aintNull 0
#define aintData int
using namespace std;
struct AINT {
int offset;
// int n;
vector<int> aint;
aintData merge (aintData a, aintData b)
{
aintData par;
par = max(a, b);
return par;
}
void build (int nod, int l, int r, vector<aintData>& a)
{
if (l >= a.size()) return;
if (l == r){
aint[nod] = a[l];
return;
}
int m = (l + r) / 2;
build(2 * nod, l, m, a);
build(2 * nod + 1, m + 1, r, a);
aint[nod] = merge(aint[2 * nod], aint[2 * nod + 1]);
}
void buildLin (vector<aintData>& a)
{
for (int i = offset; i < 2 * offset; i++)
{
aint[i] = a[i - offset + 1];
}
for (int i = offset - 1; i > 0; i--)
{
aint[i] = merge(aint[2 * i], aint[2 * i + 1]);
}
}
void initialize(int n, vector<aintData>& a)
{
offset = 1;
while (offset < n) offset *= 2;
aint.resize(2 * offset + 1);
buildLin(a);
}
void upd (int nod, int l, int r, int pos, int val)
{
if (l == r)
{
if (l == pos) {
aint[nod] = val;
}
return;
}
if (l > pos || r < pos) return;
int m = (l + r) / 2;
if (m >= pos) upd(2 * nod, l, m, pos, val);
else upd(2 * nod + 1, m + 1, r, pos, val);
aint[nod] = merge(aint[2 * nod], aint[2 * nod + 1]);
}
void updLin (int pos, int val)
{
int updPos = pos + offset - 1;
aint[updPos] = val;
updPos /= 2;
while (updPos > 0)
{
aint[updPos] = merge(aint[2 * updPos], aint[2 * updPos + 1]);
updPos /= 2;
}
}
void update (int pos, int val)
{
// upd(1, 1, offset, pos, val);
updLin(pos, val);
}
aintData qry (int nod, int l, int r, int ql, int qr)
{
if (ql <= l && r <= qr)
{
return aint[nod];
}
if (r < ql || l > qr) return aintNull;
int m = (l + r) / 2;
return merge(qry(2 * nod, l, m, ql, qr),
qry(2 * nod + 1, m + 1, r, ql, qr));
}
aintData query(int l, int r)
{
return qry(1, 1, offset, l, r);
}
};
const int nmax = 1e5 + 9;
int v[nmax];
int p[nmax];
struct HLD {
vector<int> adj[nmax];
// AINT aint;
int n;
int subtree[nmax];
int heavyChild[nmax];
vector<int> heavyOrder;
int heavyHead[nmax];
int depth[nmax];
int parent[nmax];
void calcSubtree (int nod = 1, int par = 0)
{
parent[nod] = par;
subtree[nod] = 1;
depth[nod] = depth[par] + 1;
int maxSon = 0, sonInd = -1;
for (int to : adj[nod])
{
if (to == par) continue;
calcSubtree(to , nod);
subtree[nod] += subtree[to];
if (subtree[to] > maxSon)
{
maxSon = subtree[to];
sonInd = to;
}
}
heavyChild[nod] = sonInd;
}
void heavyFirst (int nod, int par, int head)
{
heavyHead[nod] = head;
heavyOrder.push_back(nod);
if (heavyChild[nod] != -1) heavyFirst(heavyChild[nod] , nod, head);
for (auto to : adj[nod])
{
if (to != heavyChild[nod] && to != par)
{
heavyFirst(to , nod, to);
}
}
}
void printHeavyOrder ()
{
for (int i = 1; i <= n; i++)
{
cout << heavyOrder[i] << ' ';
}
for (int i = 1; i <= n; i++)
{
cout << i << ' ' << heavyHead[i] << '\n';
}
cout << '\n';
}
void initialize(int N)
{
n = N;
// int time = 0;
heavyOrder.push_back(0);
calcSubtree();
heavyFirst(1, 0, 1);
// aint.initialize(n, a);
}
int lca (int u, int v)
{
if (heavyHead[u] == heavyHead[v]) {
if (depth[u] < depth[v]) return u;
else return v;
}
if (depth[heavyHead[u]] < depth[heavyHead[v]])
{
return lca(parent[heavyHead[v]] , u);
}
else {
return lca(parent[heavyHead[u]] , v);
}
}
int query (int x, int y)
{
}
};
int main ()
{
freopen("lca.in" , "r" , stdin);
freopen("lca.out" , "w" , stdout);
int n , q; cin >> n >> q;
HLD hld;
for (int i = 2; i <= n; i++)
{
cin >> p[i];
hld.adj[p[i]].push_back(i);
}
hld.initialize(n);
while (q--)
{
int x, y; cin >> x >> y;
cout << hld.lca(x, y) << '\n';
}
/* for (int i = 1; i < n; i++)
{
int x, y; cin >> x >> y;
hld.adj[x].push_back(y);
hld.adj[y].push_back(x);
}*/
// hld.printHeavyOrder();
}