Cod sursa(job #790153)

Utilizator vendettaSalajan Razvan vendetta Data 20 septembrie 2012 16:11:08
Problema Lowest Common Ancestor Scor 100
Compilator cpp Status done
Runda Arhiva educationala Marime 3.15 kb
#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
#include <set>
#include <queue>
#include <deque>

using namespace std;

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

#define nmax 100005
#define ll long long
#define inf (1<<29)

int n, m, v[2*nmax], P[nmax], nivel[nmax], Min[19][2*nmax], Poz[19][2*nmax], Exp[2*nmax];
vector<int> gf[nmax];

void citeste(){

    f >> n >> m;//numarul de noduri; numarul de query-uri
    for(int i=1; i<n; ++i){
        int x;
        f >> x;
        gf[x].push_back(i+1);
    }

}

void dfs(int nod, int niv){

    v[ ++v[0] ] = nod;
    P[nod] = v[0];
    nivel[nod] = niv;

    for(int i=0; i<gf[nod].size(); ++i){
        int vc = gf[nod][i];
        dfs(vc, niv+1);
        v[ ++v[0] ] = nod;
    }

}

void preprocesare(){

    for(int i=0; i<=18; ++i) for(int j=1; j<=v[0]; ++j) Min[i][j] = inf;

    for(int i=1; i<=v[0]; ++i) Min[0][i] = nivel[ v[i] ], Poz[0][i] = i;

    for(int i=1; i<=18; ++i){
        for(int j=1; (j+(1<<i)-1)<=v[0]; ++j){
            int dr = j + (1<<i) - 1;//capatul dreapta al intervalului ocupat de pozitia j
            //acum caut o pozitie new_X a. i. new_X + (1<<(i-1)) -1 == dr
            int new_X = dr - (1<<(i-1)) + 1;
            //initial presupun ca minimul se afla pe intervalul [j, j+(1<<(i-1))-1]
            Min[i][j] = Min[i-1][j];
            Poz[i][j] = Poz[i-1][j];
            //acum vreau sa vad daca exista un element mai mic pe intervalul [new_X, new_X+(1<<(i-1))-1]
            if (Min[i][j] > Min[i-1][new_X]){
                Min[i][j] = Min[i-1][new_X];
                Poz[i][j] = Poz[i-1][new_X];
            }
        }
    }

}

void scrie(){

    for(int i=1; i<=v[0]; i++) g << v[i] << " ";
    g << "\n";
    for(int i=1; i<=v[0]; ++i) g<< nivel[ v[i] ] << " ";
    g << "\n";

}

void rezolva(){

    //aflu lca-ul cu rmq
    //fac o parcurgere euler a arborelui

    dfs(1, 1);

    //acum am v[] care imi da secventa euler si fac preprocesare pe aceasta secventa imi fac Min[i][j] = minimul de pe intervalul [j, j+(2^i)-1]
    //si mai tin minte Poz[i][j] = X, unde X e pozitie pe care se afla acest minin(in secventa euler)
    //scrie();
    preprocesare();

    Exp[1] = 0;
    for(int i=2; i<=v[0]; ++i) Exp[i] = Exp[i/2] + 1;



    for(int i=1; i<=m; ++i){
        int x, y;
        f >> x >> y;

        //acum Lca-ul celor doua noduri va fi dat de nodul cu cel mai mic nivel de pe intervalul P[x], P[y]
        x = P[x];  y = P[y];
        if (x > y ) swap(x,y);
        //aflu minimul in o(1)
        //iau initial intervalul x, x+(1<<k)-1 si apoi minimul de pe intervalul [x+ceva, x+ceva+(1<<k)-1]
        int lung = y-x+1;
        int k = Exp[lung];

        int rez = Min[k][x];
        int lca = v[ Poz[k][x] ];


        //acum trebuie sa caut o noua pozitie(fie aceasta Y) a. i.  Y + (1<<i)-1 == y;
        int new_X = y - (1<<k) +1;
        if (rez > Min[k][new_X]){
            rez = Min[k][new_X];
            lca = v[ Poz[k][new_X] ];
        }
        g << lca << "\n";
    }

}

int main(){

    citeste();
    rezolva();

    f.close();
    g.close();

    return 0;

}