Cod sursa(job #3129440)

Utilizator juniorOvidiu Rosca junior Data 14 mai 2023 17:18:53
Problema Heavy Path Decomposition Scor 100
Compilator cpp-64 Status done
Runda Arhiva educationala Marime 6.42 kb
/*
Acest program implementează algoritmul Heavy Path Decomposition (HPD) pentru
a rezolva probleme legate de un arbore ponderat. HPD este o tehnică de divizare
a unui arbore în lanțuri, astfel încât lungimea maximă a drumului
de la rădăcină la orice nod să fie minimizată. În acest program, arborele este
construit din noduri ponderate, iar algoritmul HPD este folosit
pentru a răspunde la interogări legate de aceste ponderi.

Programul poate fi divizat în câteva părți principale:

1. Citirea și construirea grafului.
2. Implementarea Heavy Path Decomposition.
3. Construirea și actualizarea arborelui de intervale.
4. Răspunsul la interogări.

**Citirea și construirea grafului (`read_graph`):**

Funcția `read_graph` citește numărul de noduri (n) și numărul de interogări (m),
atribuie ponderi fiecărui nod și construiește arborele
prin adăugarea muchiilor între noduri.

**Implementarea Heavy Path Decomposition (`df`, `make_paths`):**

Funcția `df` este o funcție recursivă de parcurgere în adâncime
(Depth-First Search) care calculează nivelul fiecărui nod în arbore,
dimensiunea subarborelui și descompune arborele în lanțuri.
Dacă nodul curent este o frunză, se creează un nou lanț.
Dacă nu, nodul curent este adăugat la lanțul nodului "heavy"
(cel cu cel mai mare subarbore).

Funcția `make_paths` apelează funcția `df` pentru a realiza descompunerea
și apoi construiește arborele de intervale pentru fiecare lanț.

**Construirea și actualizarea arborelui de intervale (`build`, `update`):**

Funcția `build` construiește arborele de intervale pentru un lanț dat.
Fiecare nod din arborele de intervale conține valoarea maximă
dintre cele două copii.
Funcția `update` este utilizată pentru a actualiza un nod
din arborele de intervale și, implicit, toți strămoșii săi.

**Răspunsul la interogări (`query`, `solve`):**

Funcția `query` interoghează arborele de intervale pentru a găsi
valoarea maximă dintre două noduri pe același lanț.
Funcția `solve` procesează interogările și răspunde la acestea folosind
funcțiile `query` și `update`.

Există două tipuri de interogări:
- Actualizarea valorii unui nod (t = 0).
- Aflarea valorii maxime pe drumul dintre două noduri (t = 1).

Pentru a răspunde la o interogare de tipul 1,
programul parcurge lanțurile de la nodul x la nodul y,
interogând arborele de intervale pentru a obține valoarea maximă
pe fiecare lanț și actualizând soluția cu aceasta.
Procesul se repetă până când nodurile x și y se află pe același lanț.

În cele din urmă, programul afișează răspunsul la interogările de tipul 1.
*/

#include <algorithm>
#include <cstdio>
#include <fstream>
#include <vector>

using namespace std;

#define MAXN 100010

int n, m, lanturi;
int a[MAXN], niv[MAXN], g[MAXN], nl[MAXN], aint[4 * MAXN];
bool v[MAXN];
int lTata[MAXN], lNiv[MAXN], d[MAXN], pozl[MAXN];
vector<int> G[MAXN], l[MAXN];
ifstream fin("heavypath.in");

void read_graph() {
    int x, y;
    fin >> n >> m;
    for (int i = 1; i <= n; i++)
        fin >> a[i];
    for (int i = 1; i < n; i++) {
        fin >> x >> y;
        G[x].push_back(y);
        G[y].push_back(x);
    }
}

void df(int nod) {
    int hN = -1;
    bool frunza = true;

    v[nod] = true, g[nod] = 1;
    for (int vecin : G[nod]) {
        if (!v[vecin]) {
            frunza = false;
            niv[vecin] = niv[nod] + 1;
            df(vecin);
            g[nod] += g[vecin];
            if (hN == -1)
                hN = vecin;
            else if (g[hN] < g[vecin])
                hN = vecin;
        }
    }
    if (frunza) {
        nl[nod] = ++lanturi;
        d[lanturi] = 1;
        l[lanturi].push_back(nod);
        return;
    }
    nl[nod] = nl[hN];
    d[nl[nod]]++;
    l[nl[nod]].push_back(nod);
    for (int vecin : G[nod]) {
        if (vecin != hN) {
            lTata[nl[vecin]] = nod;
            lNiv[nl[vecin]] = niv[nod];
        }
    }
}

void build(int nod, int s, int d, int decalaj, int lant) {
    int m;
    if (s < d) {
        m = (s + d) / 2;
        build(nod * 2, s, m, decalaj, lant);
        build(nod * 2 + 1, m + 1, d, decalaj, lant);
        aint[nod + decalaj] = max(aint[nod * 2 + decalaj], aint[nod * 2 + 1 + decalaj]);
    }
    else
        aint[nod + decalaj] = a[l[lant][s - 1]];
}

void make_paths() {
    niv[1] = 1;
    df(1);
    for (int i = 1; i <= lanturi; i++) {
        reverse(l[i].begin(), l[i].end());
        if (i > 1)
            pozl[i] = pozl[i - 1] + d[i - 1] * 4;
        build(1, 1, d[i], pozl[i], i);
    }
}

void update(int nod, int left, int right, int poz, int val, int decalaj) {
    if (left == right) {
        aint[nod + decalaj] = val;
        return;
    }
    int med = (left + right) / 2;
    if (poz <= med)
        update(nod * 2, left, med, poz, val, decalaj);
    else
        update(nod * 2 + 1, med + 1, right, poz, val, decalaj);
    aint[nod + decalaj] = max(aint[nod * 2 + decalaj], aint[nod * 2 + 1 + decalaj]);
}

int query(int nod, int left, int right, int qleft, int qright, int decalaj) {
    if (qleft <= left && right <= qright)
        return aint[nod + decalaj];
    int med = (left + right) / 2, rez = 0;
    if (qleft <= med)
        rez = max(rez, query(nod * 2, left, med, qleft, qright, decalaj));
    if (med < qright)
        rez = max(rez, query(nod * 2 + 1, med + 1, right, qleft, qright, decalaj));
    return rez;
}

void solve() {
    int t, x, y, sol = 0;
    for (int i = 1; i <= m; ++i) {
        fin >> t >> x >> y;
        if (t == 0)
            update(1, 1, d[nl[x]], niv[x] - lNiv[nl[x]], y, pozl[nl[x]]);
        else {
            sol = 0;
            while (1) {
                if (nl[x] == nl[y]) {
                    if (niv[x] > niv[y])
                        swap(x, y);
                    sol = max(sol, query(1, 1, d[nl[x]], niv[x] - lNiv[nl[x]], niv[y] - lNiv[nl[x]], pozl[nl[x]]));
                    break;
                }
                if (lNiv[nl[x]] < lNiv[nl[y]])
                    swap(x, y);
                sol = max(sol, query(1, 1, d[nl[x]], 1, niv[x] - lNiv[nl[x]], pozl[nl[x]]));
                x = lTata[nl[x]];
            }
            printf("%d\n", sol);
        }
    }
}

int main() {
    freopen("heavypath.out", "w", stdout);
    read_graph();
    make_paths();
    solve();
    return 0;
}