Cod sursa(job #1693019)

Utilizator Burbon13Burbon13 Burbon13 Data 22 aprilie 2016 09:50:55
Problema Heavy Path Decomposition Scor 100
Compilator cpp Status done
Runda Arhiva educationala Marime 7.97 kb
#include <cstdio>
#include <vector>
#include <bitset>
#include <algorithm>

using namespace std;

const int nmx = 100002;           /// numar maxim de noduri

int n,m;                          /// numar de noduri, numar de operatii
bitset <nmx> viz;                 /// viz[i] = true - nodul fu vizitat <-> false - nu fu
vector <int> lant[nmx], g[nmx];   /// lanturile si graful
vector <int> aint[nmx];           /// arborele de intervale
int val[nmx];                     /// val[i] = costul nodului i
int care_lant[nmx];               /// care_lant[i] = lantul in care se afla nodul i
int poz_lant[nmx];                /// poz_lant[i] = pozitia nodului i in lant
int tata_lant[nmx];               /// tata_lant[i] = tatal lantului i
int nr_lanturi;                  /// numarul total de lanturi
int greutate[nmx];                /// greutate[i] = greutatea nodului i
int adancime[nmx];                /// adancimea[i] = adancimea nodului i

void citire() {
    scanf("%d %d", &n, &m);

    for(int i = 1; i <= n; ++i)
        scanf("%d", &val[i]);

    for(int i = 1; i < n; ++i) {
        int nod1, nod2;
        scanf("%d %d", &nod1, &nod2);
        g[nod1].push_back(nod2);
        g[nod2].push_back(nod1);
    }
}

void parcurgere(int nod) {
    viz[nod] = true;              /// marchez nodul ca fiind vizitat
    greutate[nod] = 1;            /// setez greutatea subarborelui cu radacina nod cu 1

    bool frunza = true;           /// presupunem ca nodul este frunza
    int nod_max = -1;             /// variabila in care retinem fiul cel mai greu (grasut)

    for(vector<int>::iterator it = g[nod].begin(); it != g[nod].end(); ++it) {
        if(viz[*it] == true)      /// verificam daca nodul este tatal
            continue;             /// caz afirmativ, sarim peste el

        frunza = false;                    /// in acest caz nodul nu este frunza
        adancime[*it] = adancime[nod] + 1; /// setam adancimea fiului

        parcurgere(*it);                ///  continuam parcurgerea pe fiu

        greutate[nod] += greutate[*it]; /// adaugam greutatea subarborelui cu radacina *it

        if(nod_max == -1)               /// initializam nodul cel mai greu cu primul
            nod_max = *it;
        else if(greutate[*it] > greutate[nod_max]) /// verificam daca nodul acesta este mai greu
            nod_max = *it;                         /// actualizam cel mai greu nod din toti fii
    }

    if(frunza == true) {                 /// daca nod este frunza, adaugam un nou lant
        ++ nr_lanturi;                   /// incrementam numarul de lanturi
        care_lant[nod] = nr_lanturi;     /// memoram in ce lant se afla nodul
        lant[nr_lanturi].push_back(nod); /// adaugam nodul in noul lant
        return;
    }

    care_lant[nod] = care_lant[nod_max]; /// lantul in care se afla nodul este acelasi cu fiul cel mai greu
    lant[care_lant[nod]].push_back(nod); /// adaugam nodul in lantul copilului cel mai gras

    for(vector<int>::iterator it = g[nod].begin(); it != g[nod].end(); ++it) {
        if(*it == nod_max || adancime[*it] < adancime[nod]) /// daca nodul este cel mai gras sau
            continue;                                       /// tatal, sarim peste

        tata_lant[care_lant[*it]] = nod; /// actualizam tatal lanturilor care se termina in *it
    }
}

void construieste(int nod, int stanga, int dreapta, int nr_lant) {
    if(stanga == dreapta) {
        aint[nr_lant][nod] = val[lant[nr_lant][stanga - 1]];   /// am gasit pozitia in arborele de intervale si actualizam
        int aux = aint[nr_lant][nod];
        return;                                                /// ne intoarcem
    }

    int mij = (stanga + dreapta) / 2;                          /// setam pivotul

    construieste(2 * nod, stanga, mij, nr_lant);               /// construim in stanga
    construieste(2 * nod + 1, mij + 1, dreapta, nr_lant);      /// construim in dreapta

    aint[nr_lant][nod] = max(aint[nr_lant][2*nod],aint[nr_lant][2*nod+1]); /// actualizam baa
}

void creaza_lanturi() {             /// construieste arborele de intervale
    adancime[1] = 1;                /// setam adancimea radacinii
    parcurgere(1);                  /// parcurgem in adancime arborele

    for(int i = 1; i <= nr_lanturi; ++i) {         /// iteram prin lanturile create
        reverse(lant[i].begin(),lant[i].end());    /// inversam ordinea nodurilor din lant ca sa fie
        /// de la frunza la radacina

        /// afisare intermediara a lanturilor grasute
        /**for(vector<int>::iterator it = lant[i].begin(); it != lant[i].end(); ++it)
            printf("%d ", *it);
        printf("\n");**/

        int paux = 0;

        for(vector<int>::iterator it = lant[i].begin(); it != lant[i].end(); ++it)
            poz_lant[*it] = ++paux;

        aint[i].resize(lant[i].size()*4 + 2);     /// alocam spatiu necesar ptr arborele lantului i

        construieste(1,1,lant[i].size(),i);   /// construim arborele de intervale pentru lantul i
    }
}

void update(int nod, int stanga, int dreapta, int nr_lant, int p_lant, int update_val) {
    /// nr_lant = numarul lantului in care se afla nodul
    /// p_lant = pozitia in lant a nodului
    /// update_val = valoarea pe care o atribuim

    if(stanga == dreapta) {               /// am gasit pozitia de updatat
        aint[nr_lant][nod] = update_val;  /// actualizam
        return;
    }

    int mij = (stanga + dreapta) / 2;

    if(p_lant <= mij)                     /// vedem unde tre sa cautam pozitia
        update(2 * nod, stanga, mij, nr_lant, p_lant, update_val);
    else
        update(2 * nod + 1, mij + 1, dreapta, nr_lant, p_lant, update_val);

    aint[nr_lant][nod] = max(aint[nr_lant][2 * nod],aint[nr_lant][2 * nod + 1]);
}

int query(int nod, int stanga, int dreapta, int qstanga, int qdreapta, int nr_lant) {
    if(qstanga <= stanga && qdreapta >= dreapta){
        int aux = aint[nr_lant][nod];
        return aint[nr_lant][nod];
    }

    int mij = (stanga + dreapta) / 2;
    int rez = 0;  /// variabila in care retinem valoarea maxima din query-urile ce urmeaza

    if(qstanga <= mij)
        rez = max(rez, query(2 * nod, stanga, mij, qstanga, qdreapta, nr_lant));
    if(qdreapta > mij)
        rez = max(rez, query(2 * nod + 1, mij + 1, dreapta, qstanga, qdreapta, nr_lant));

    return rez;
}

void rezolva() {
    int tip, x, y;
    for(int i = 1; i <= m; ++i) {          /// iteram operatiile
        scanf("%d %d %d", &tip, &x, &y);

        if(tip == 0){
            val[x] = y;
            update(1,1,lant[care_lant[x]].size(),care_lant[x],poz_lant[x],y);
        }
        else {
            int sol = 0; /// memoram valoarea maxima cautata

            while(true) {
                if(care_lant[x] == care_lant[y]) {
                    if(adancime[x] - adancime[tata_lant[care_lant[x]]] < adancime[y] - adancime[tata_lant[care_lant[y]]])
                        sol = max(sol,query(1,1,lant[care_lant[x]].size(),adancime[x] - adancime[tata_lant[care_lant[x]]],
                                            adancime[y] - adancime[tata_lant[care_lant[y]]], care_lant[x]));
                    else
                        sol = max(sol,query(1,1,lant[care_lant[x]].size(),adancime[y] - adancime[tata_lant[care_lant[y]]],
                                            adancime[x] - adancime[tata_lant[care_lant[x]]], care_lant[x]));
                    break;
                }

                if(adancime[tata_lant[care_lant[x]]] < adancime[tata_lant[care_lant[y]]])
                    swap(x,y);

                sol = max(sol,query(1,1,lant[care_lant[x]].size(), 1, adancime[x] - adancime[tata_lant[care_lant[x]]], care_lant[x]));

                x = tata_lant[care_lant[x]];
            }

            printf("%d\n", sol);
        }
    }
}

int main() {
    freopen("heavypath.in", "r", stdin);
    freopen("heavypath.out", "w", stdout);

    citire();

    creaza_lanturi();

    rezolva();

    return 0;
}