#include <vector>
#include <queue>
#include <algorithm>
#include <fstream>
#include <cmath>
using namespace std;
struct node {
int valoare;
node* next;
};
void adaugareInceput(node*& head, node*& last, int x)
{
node* element = new node;
element->valoare = x;
///element->next=NULL;
element->next = head; //devine NULL daca head este NULL
if (head == NULL) //separat cazul in care lista este vida-> se actualizeaza si last
last = element;
head = element;
}
void adaugareFinal(node*& head, node*& last, int x)
{
node* element = new node;
element->valoare = x;
element->next = NULL;
if (head == NULL) //separat cazul in care lista este vida
head = element;
else last->next = element;
last = element;
}
void stergereInceput(node*& head, node*& last)
{
if (head == NULL)
return;
node* p = head;
head = head->next;
delete p;
if (head == NULL) //cazul cand lista ramane vida- se modifica si last
last = NULL;
}
void stergereFinal(node*& head, node*& last)
{
node* p = head;
if (head == NULL)
return;
if (head == last) //lista cu un element, echivalent cu head->next==NULL
{
delete head;
head = NULL;
last = NULL;
return;
}
while (p->next->next) //ne pozitionam pe penultimul element pentru a il sterge pe ultimul
{
p = p->next;
}
delete p->next;
last = p;
p->next = NULL;
}
void inserare(node*& head, node*& last, int x, int poz)
{
if (poz == 0)
{
adaugareInceput(head, last, x);
return;
}
/*!!separat cazul de lista vida in care vrem sa inseram pe pozitie>0*/
if (head == NULL)
return;
int cnt = 0;
node* p = head;
while (cnt < poz - 1) //ne pozitionam inainte sa inseram, puteam folosi si for
{
p = p->next;
if (p == NULL) //daca am iesit din lista poz este prea mare->nu inseram
{
return;
}
cnt++;
}
node* q = new node;
q->valoare = x;
q->next = NULL;
q->next = p->next;
p->next = q;
if (q->next == NULL) //am adaugat la final daca dupa noul nod nu urmeaza alt nod
last = q;
}
void stergere(node*& head, node*& last, int poz)
{
if (poz == 0)
{
stergereInceput(head, last);
return;
}
/*!!separat cazul de lista vida */
if (head == NULL)
return;
int cnt = 0;
node* p = head;
while (cnt < poz - 1) //ne pozitionam tot inaine de elementul de sters
{
p = p->next;
if (p == NULL) //daca am iesit din lista poz este prea mare->nu inseram
{
return;
}
cnt++;
}
//suntem pozitionati inainte de poz
//stergem p->next, daca exista
if (p->next) {
node* q;
q = p->next; //elementul care va fi sters
p->next = p->next->next;
delete q;
if (p->next == NULL)
last = p;
}
}
void afisare(node* head)
{
node* p = head;
while (p)
{
cout << p->valoare << ' ';
p = p->next;
}
cout << endl;
}
struct Compare {
bool operator()(pair<int, int> a, pair<int, int> b) {
return a.first > b.first; // This will create a min heap
}
bool operator()(vector <int> v1, vector<int> v2) {
if (v1[0] == v2[0]) {
return v1[1] < v2[1];
}
else return v1[0] < v2[0];
}
};
node* mergeKLists(vector<node*>& lists) {
priority_queue<pair<int, int> , vector<pair<int, int> >, Compare > pq; /// value and list index
node* rez = nullptr;
node* last = nullptr;
for (int i = 0; i < lists.size(); i++) {
if (lists[i] != nullptr) {
pq.push({ lists[i]->valoare, i });
}
}
while (!pq.empty()) {
pair<int, int> currVal = pq.top();
pq.pop();
adaugareFinal(rez, last, currVal.first);
if (lists[currVal.second]->next != nullptr) {
lists[currVal.second] = lists[currVal.second]->next;
pq.push({ lists[currVal.second]->valoare, currVal.second });
}
}
return rez;
}
int minGroups(vector<vector<int>>& intervals) {
priority_queue<int, vector<int>, greater<int>> pq;
sort(intervals.begin(), intervals.end(), Compare());
for (int i = 0; i < intervals.size(); i++) {
if (!pq.empty() && pq.top() < intervals[i][0])
pq.pop();
pq.push(intervals[i][1]);
}
return pq.size();
}
const int NMAX = 100005;
const int LMAX = 20;
int k, n, m;
int nivel[NMAX << 1], euler[NMAX << 1], prima_apar[NMAX]; //euler tur- 2*nr muchii
int rmq[NMAX << 1][20];
node* G[NMAX];
node* lastG[NMAX];
void parcurg(int nod, int niv_curent) {
k = k + 1; //// index pentru reprezentarea euler
euler[k] = nod; /// nodul curent se pune la pozitia k in turul euler
nivel[k] = niv_curent; /// nivelul nodului curent in arbore
prima_apar[nod] = k; /// prima aparitie a nodului in turul euler
node* p = G[nod];
while (p != NULL) { /// parcurgem fii nodului actual (descendentii sai)
parcurg(p->valoare, niv_curent + 1); /// apelam recursiv parcurgerea pentru fiecare fiu
/// la intoarcerea din recursivitate adaugam "de jos in sus" in turul euler
k = k + 1;
euler[k] = nod;
nivel[k] = niv_curent;
p = p->next; /// mergem la urmatorul descendent
}
}
void prepr_rmq() {
for (int i = 0; i <= k; i++)
rmq[i][0] = i;
for (int j = 1; (1 << j) <= k; j++) /// 1 << j = 2^j
for (int i = 0; i + (1 << j) - 1 < k; i++) /// i + 2^j - 1 < n <=> i < n - 2^j + 1
{
int l = 1 << (j - 1); /// 2^(j-1)
if (nivel[rmq[i][j - 1]] < nivel[rmq[i + l][j - 1]])
rmq[i][j] = rmq[i][j - 1];
else
rmq[i][j] = rmq[i + l][j - 1];
///rmq[i][j] = min(rmq[i][j - 1], rmq[i + l][j - 1]); /// rmq[i][j] = the minimum of the interval [i, i + 2^(j-1) - 1] and the minimum of the interval [i + 2^(j-1), i + 2^j - 1]
/// it consists of the minimum of the interval [i, i + 2^(j-1) - 1] and the minimum of the interval [i + 2^(j-1), i + 2^j - 1]
}
}
int query(int x, int y) {
int k = log2(y - x + 1); /// 2^k <= y - x + 1
int l = 1 << k; /// 2^k
if (nivel[rmq[x][k]] < nivel[rmq[y - l + 1][k]])
return euler[rmq[x][k]];
else
return euler[rmq[y - l + 1][k]];
/// return min(rmq[x][k], rmq[y - l][k]); /// the minimum of the interval [x, y] is the minimum of the interval [x, x + 2^k - 1] and the minimum of the interval [y - 2^k + 1, y]
}
int main() {
ifstream cin("lca.in");
ofstream cout("lca.out");
int x;
cin >> n >> m;
for (int i = 1; i <= n; i++)
G[i] = lastG[i] = NULL;
for (int i = 2; i <= n; i++) {
cin >> x;
adaugareFinal(G[x], lastG[x], i);
}
k = -1;
parcurg(1, 0);
prepr_rmq();
for (int i = 0; i < m; i++) {
int x, y;
cin >> x >> y;
if (prima_apar[x] > prima_apar[y])
swap(x, y);
cout << query(prima_apar[x] , prima_apar[y] ) << endl;
}
}