Cod sursa(job #2292095)

Utilizator CristyXtremeSimion Cristian CristyXtreme Data 28 noiembrie 2018 22:49:18
Problema Lowest Common Ancestor Scor 10
Compilator cpp-64 Status done
Runda Arhiva educationala Marime 3.52 kb
/*  Facem o Parcurgere Euler (Euler Tour) a grafului - link util: https://infoarena.ro/lowest-common-ancestor
    Retinem adancimea la fiecare nod parcurs, si prima pozitie pe care intalnim un nod
    Aplicam RMQ (vezi link-ul de sus) intre nodurile pentru care vrem sa aflam LCA, cu o observatie:
    LCA(i, j) = RMQ(prima_pozitie[i], prima_pozitie[j]). Query-ul pentru care cautam minimul este pentru vectorul de adancime
*/
#include <stdio.h>
#include <vector>

using namespace std;

// declaram variabilele necesare pentru RMQ
int minim[18][199999] = {0}, minpos[18][199999];
int tata[100001], prima_poz[100001], adancime[199999], euler[199999];

void tur_euler(int rad, int adanc, int &lungime_parcurgere, int *euler, int *tata, vector <int> *fii, int *prima_poz, int *adancime) {
    // verificam si actualizam prima pozitie
    if (!prima_poz[rad])
        prima_poz[rad] = lungime_parcurgere;
    // daca nodul este frunza doar il adaugam
    // daca are fii lucrurile se complica: trebuie sa ne intoarcem la radacina dupa fiecare fiu parcurs
    for (int i = 0; i < fii[rad].size(); i++) {
        adancime[lungime_parcurgere] = adanc;
        euler[lungime_parcurgere++] = rad;
        tur_euler(fii[rad][i], adanc + 1, lungime_parcurgere, euler, tata, fii, prima_poz, adancime);
    }
    adancime[lungime_parcurgere] = adanc;
    euler[lungime_parcurgere++] = rad;
}

int min(int a, int b) {
    if (a > b)
        return b;
    return a;
}

int RMQ(int l, int r, int *euler, int *adancime) {
    // asiguram ca o sa cautam de la stanga la dreapta
    if (l > r) {
        int aux = l;
        l = r;
        r = aux;
    }
    int i, k = 0, sol, solpos;
    // calculam cea mai mare putere de 2 pentru care putem sa accesam matricea RMQ
    for (i = 1; i <= (r - l); i <<= 1)
        k++;
    k--;
    if (k < 0)
        k = 0;
    // presupunem ca solutia este in "prima parte", apoi cautam in cealalta bucata
    sol = minim[k][l];
    solpos = minpos[k][l];
    if (sol > minim[k][l + (1 << k)])
        solpos = minpos[k][l + (1 << k)];
    return euler[solpos];
}

int main() {
	freopen("lca.in", "r", stdin);
	freopen("lca.out", "w", stdout);
	int n, m, lungime_parcurgere = 0, x, y, k = 0;
	scanf("%d %d", &n, &m);
    vector <int> fii[n + 1];
    // adaugam tatii si fii
    for (int i = 2; i <= n; i++) {
        scanf("%d", &tata[i]);
        fii[tata[i]].push_back(i);
    }
    // parcurgem euler de la radacina trimitand adancimea
    tur_euler(1, 0, lungime_parcurgere, euler, tata, fii, prima_poz, adancime);
    // calculam logaritm in baza 2 din (2n - 1) pentru construirea matricei de RMQ pe baza vectorului de adancime
    for (int i = 1; i <= 2 * n - 1; i <<= 1)
        k++;
    // initializam prima linie pentru a putea face recurenta
    for (int i = 0; i < 2 * n - 1; i++) {
        minim[0][i] = adancime[i];
        minpos[0][i] = i;
    }
    // construim restul tabloului pe baza recursivitatii
    for (int j = 1; j < k; j++)
        for (int i = 0; i < 2 * n - 1; i++)
            if (minim[j - 1][i] > minim[j - 1][i + (1 << (j - 1))]) {
                minim[j][i] = minim[j - 1][i + (1 << (j - 1))];
                minpos[j][i] = minpos[j - 1][i + (1 << (j - 1))];
            }
            else {
                minim[j][i] = minim[j - 1][i];
                minpos[j][i] = minpos[j - 1][i];
            }
    for (int i = 0; i < m; i++) {
        scanf("%d %d", &x, &y);
        printf("%d\n", RMQ(prima_poz[x], prima_poz[y], euler, adancime));
    }
	return 0;
}