Cod sursa(job #3161271)

Utilizator AlexInfoIordachioaiei Alex AlexInfo Data 26 octombrie 2023 13:21:46
Problema Lowest Common Ancestor Scor 0
Compilator cpp-64 Status done
Runda Arhiva educationala Marime 3.12 kb
#include <bits/stdc++.h>

using namespace std;

ifstream in("lca.in");
ofstream out("lca.out");

typedef unsigned long long ull;
typedef long long ll;
typedef pair<int, int> pii;
#define fi first
#define se second

#define NMAX 100005
#define MOD 1000000007
#define INF 0x3f3f3f3f

int n, m, cnt = 0, euler[NMAX * 2], adancimi[NMAX * 2], prap[NMAX];
bool visited[NMAX];
vector<int> minlevel(8 * NMAX), eulerST(8 * NMAX);
vector<vector<int>> tree(NMAX);
// minlevel este arbore de intervale - contine minimul adancimilor dintr-un interval in parcurgerea euler
// LCA(a, a) reprezinta minimul din intervalul [a, b] in parcurgerea euler si este Lowest Common Ancestor
// unde a, respectiv b reprezinta prima aparitie a nodurilor x, respectiv y in parcurgerea euler

void read()
{
    int root;
    in >> n >> m;
    for (int i = 2; i <= n; i++) // pt fiecare nod citesc radacina si adaug muchie intre ele
        in >> root, tree[root].push_back(i);
}

void Euler(int nod, int level)
{
    if (visited[nod])
        return;

    visited[nod] = 1;
    euler[++cnt] = nod;
    prap[nod] = cnt;       // prima aparitie a fix inainte de a fi vizitat prima data, nu in for
    adancimi[cnt] = level; // adancimea corespunzatoare nodului in parcurgerea euler a nodului
    for (auto v : tree[nod])
        Euler(v, level + 1), euler[++cnt] = nod, adancimi[cnt] = level;
    // cand ma intorc dupa ce am parcurs toate nodurile din partea unui fiu il adaug iar
}

void buildLevels(int node = 1, int left = 1, int right = cnt)
{
    if (left == right)
    {
        minlevel[node] = adancimi[left], eulerST[node] = euler[left];
        return;
    }

    int mid = (left + right) / 2;
    buildLevels(2 * node, left, mid);
    buildLevels(2 * node + 1, mid + 1, right);

    if (minlevel[2 * node] < minlevel[2 * node + 1])
        minlevel[node] = minlevel[2 * node], eulerST[node] = eulerST[2 * node];
    else
        minlevel[node] = minlevel[2 * node + 1], eulerST[node] = eulerST[2 * node + 1];
}

pii LCA(int a, int b, int node = 1, int left = 1, int right = cnt)
{
    if (a <= left && right <= b) // intervalul verificat acum [left, right] este inclus in [a, b], cel de care avem nevoie
        return {minlevel[node], eulerST[node]};

    int mid = (left + right) / 2;
    pii ans = {INF, 0};
    if (a <= mid)
    { // daca mergand in stanga inca ma aflu in interval
        pii l = LCA(a, b, 2 * node, left, mid);
        if (l.fi < ans.fi)
            ans = l;
    }
    if (mid + 1 <= b)
    { // daca mergand in dreapta inca ma aflu in interval
        pii r = LCA(a, b, 2 * node + 1, mid + 1, right);
        if (r.fi < ans.fi)
            ans = r;
    }

    return ans;
}

void solve()
{
    Euler(1, 1); // parcurgerea Euler a arborelui incepand din radacina (nodul 1)
    buildLevels();
    for (int i = 1; i <= m; i++)
    {
        int x, y;
        in >> x >> y;
        if (prap[x] <= prap[y])
            out << LCA(prap[x], prap[y]).se << ' ';
        else
            out << LCA(prap[y], prap[x]).se << ' ';
    }
}

int main()
{
    read();
    solve();
    return 0;
}