Cod sursa(job #3146820)

Utilizator Razvan48Capatina Razvan Nicolae Razvan48 Data 22 august 2023 16:46:34
Problema Heavy Path Decomposition Scor 60
Compilator cpp-64 Status done
Runda Arhiva educationala Marime 8.26 kb
#include <fstream>
#include <vector>

/// asta e varianta unde heavy child-ul este ales dupa adancimea maxima a subarborelui
/// n * sqrt n * log n
/// ia doar 60 de puncte

using namespace std;

const int NMAX = 1e5;
int val[1 + NMAX];

vector<int> graf[1 + NMAX];
bool vizitat[1 + NMAX];
int adancime[1 + NMAX];
vector<int> liniarizare;
int primaAparitie[1 + NMAX];

const int LMAX = 2 * NMAX - 1;

int aintLCA[1 + 4 * LMAX];

int heaviness[1 + NMAX]; /// ori dimensiunea subarborelui, ori lungimea maxima a unui lant ce pleaca din radacina subarborelui in jos
/// cica daca fac cu lungimea maxima a unui lant atunci ar fi complexitate n * sqrt n * log n in loc de n * log n * log n pentru dimensiune subarbore, deci putin mai slab.

int indexPath[1 + NMAX];
int indexInLiniarizarePaths[1 + NMAX]; /// este pozitie globala in singura liniarizare pe toate path-urile simultan (+ aint)

/// am facut lca-ul cu aint in loc de rmq ca sa castig niste memorie ca e tight rau la problema asta
/// nu conteaza aici ca oricum complexitatea finala e n * log n * log n indiferent daca e rmq sau aint.

struct Path
{
public:

    int indexTataPath;
    int indexTataNodPath;
    int indexInceput;

    Path() :
        indexTataPath(-1), indexTataNodPath(-1), indexInceput(-1)
        {

        }
};

vector<Path> paths;
vector<int> liniarizarePaths;
vector<int> aintLiniarizarePaths;

void dfs(int nod, int adanc)
{
    vizitat[nod] = true;
    adancime[nod] = adanc;
    liniarizare.emplace_back(nod);
    primaAparitie[nod] = (int)liniarizare.size() - 1;

    heaviness[nod] = 1;

    for (int i = 0; i < graf[nod].size(); ++i)
    {
        if (!vizitat[graf[nod][i]])
        {
            dfs(graf[nod][i], adanc + 1);
            liniarizare.emplace_back(nod);

            /// heaviness[nod] += heaviness[graf[nod][i]]; ///
            heaviness[nod] = max(heaviness[nod], heaviness[graf[nod][i]] + 1);
        }
    }

    /// ++heaviness[nod]; ///
}

void buildAintLCA(int index, int st, int dr)
{
    if (st == dr)
    {
        aintLCA[index] = liniarizare[st];
        return;
    }

    int mij = (st + dr) / 2;
    int fiuSt = 2 * index;
    int fiuDr = 2 * index + 1;

    buildAintLCA(fiuSt, st, mij);
    buildAintLCA(fiuDr, mij + 1, dr);

    if (adancime[aintLCA[fiuSt]] < adancime[aintLCA[fiuDr]])
        aintLCA[index] = aintLCA[fiuSt];
    else
        aintLCA[index] = aintLCA[fiuDr];
}

int queryAintLCA(int index ,int st, int dr, int stQ, int drQ)
{
    if (st == stQ && drQ == dr)
        return aintLCA[index];

    int mij = (st + dr) / 2;
    int fiuSt = 2 * index;
    int fiuDr = 2 * index + 1;

    if (drQ <= mij)
        return queryAintLCA(fiuSt, st, mij, stQ, drQ);
    else if (stQ >= mij + 1)
        return queryAintLCA(fiuDr, mij + 1, dr, stQ, drQ);
    else
    {
        int sol1 = queryAintLCA(fiuSt, st, mij, stQ, mij);
        int sol2 = queryAintLCA(fiuDr, mij + 1, dr, mij + 1, drQ);

        if (adancime[sol1] < adancime[sol2])
            return sol1;
        else
            return sol2;
    }
}

int LCA(int nod1, int nod2)
{
    if (primaAparitie[nod1] > primaAparitie[nod2])
        swap(nod1, nod2);

    return queryAintLCA(1, 1, (int)liniarizare.size() - 1, primaAparitie[nod1], primaAparitie[nod2]);
}

void dfsPath(int nod)
{
    vizitat[nod] = true;
    liniarizarePaths.emplace_back(nod);
    indexPath[nod] = (int)paths.size() - 1;
    indexInLiniarizarePaths[nod] = (int)liniarizarePaths.size() - 1;

    int maxHeaviness = 0;
    int indexFiuMaxHeaviness = -1;

    for (int i = 0; i < graf[nod].size(); ++i)
    {
        if (!vizitat[graf[nod][i]])
        {
            if (maxHeaviness < heaviness[graf[nod][i]])
            {
                maxHeaviness = heaviness[graf[nod][i]];
                indexFiuMaxHeaviness = i;
            }
        }
    }

    if (indexFiuMaxHeaviness != -1)
    {
        swap(graf[nod][0], graf[nod][indexFiuMaxHeaviness]);
        dfsPath(graf[nod][0]);
    }

    for (int i = 1; i < graf[nod].size(); ++i)
    {
        if (!vizitat[graf[nod][i]])
        {
            paths.emplace_back();
            paths.back().indexTataPath = indexPath[nod];
            paths.back().indexTataNodPath = nod;
            paths.back().indexInceput = (int)liniarizarePaths.size() - 1 + 1;
            dfsPath(graf[nod][i]);
        }
    }
}

void buildAintLiniarizarePaths(int index, int st, int dr)
{
    if (st == dr)
    {
        aintLiniarizarePaths[index] = val[liniarizarePaths[st]];
        return;
    }

    int mij = (st + dr) / 2;
    int fiuSt = 2 * index;
    int fiuDr = 2 * index + 1;

    buildAintLiniarizarePaths(fiuSt, st, mij);
    buildAintLiniarizarePaths(fiuDr, mij + 1, dr);

    aintLiniarizarePaths[index] = max(aintLiniarizarePaths[fiuSt], aintLiniarizarePaths[fiuDr]);
}

void updateAintLiniarizarePaths(int index, int st, int dr, int poz, int val)
{
    if (st == dr)
    {
        aintLiniarizarePaths[index] = val;
        return;
    }

    int mij = (st + dr) / 2;
    int fiuSt = 2 * index;
    int fiuDr = 2 * index + 1;

    if (poz <= mij)
        updateAintLiniarizarePaths(fiuSt, st, mij, poz, val);
    else
        updateAintLiniarizarePaths(fiuDr, mij + 1, dr, poz, val);

    aintLiniarizarePaths[index] = max(aintLiniarizarePaths[fiuSt], aintLiniarizarePaths[fiuDr]);
}

int queryAintLiniarizarePaths(int index, int st, int dr, int stQ, int drQ)
{
    if (st == stQ && drQ == dr)
        return aintLiniarizarePaths[index];

    int mij = (st + dr) / 2;
    int fiuSt = 2 * index;
    int fiuDr = 2 * index + 1;

    if (drQ <= mij)
        return queryAintLiniarizarePaths(fiuSt, st, mij, stQ, drQ);
    else if (stQ >= mij + 1)
        return queryAintLiniarizarePaths(fiuDr, mij + 1, dr, stQ, drQ);
    else
        return max(queryAintLiniarizarePaths(fiuSt, st, mij, stQ, mij), queryAintLiniarizarePaths(fiuDr, mij + 1, dr, mij + 1, drQ));
}

int solutiePeLant(int x, int y) /// y e undeva mai sus de x => indexInLiniarizarePaths[y] <= indexInLiniarizarePaths[x] mereu
{
    int sol = val[y];

    while (x != y)
    {
        if (indexPath[x] == indexPath[y])
        {
            sol = max(sol, queryAintLiniarizarePaths(1, 1, (int)liniarizarePaths.size() - 1, indexInLiniarizarePaths[y], indexInLiniarizarePaths[x]));
            x = y;
        }
        else
        {
            sol = max(sol, queryAintLiniarizarePaths(1, 1, (int)liniarizarePaths.size() - 1, paths[indexPath[x]].indexInceput, indexInLiniarizarePaths[x]));
            x = paths[indexPath[x]].indexTataNodPath;
        }
    }

    return sol;
}

int main()
{
    ifstream in("heavypath.in");
    ofstream out("heavypath.out");

    ios_base::sync_with_stdio(false);
    in.tie(nullptr);

    int n, m;
    in >> n >> m;

    for (int i = 1; i <= n; ++i)
        in >> val[i];

    for (int i = 2; i <= n; ++i)
    {
        int a, b;
        in >> a >> b;

        graf[a].emplace_back(b);
        graf[b].emplace_back(a);
    }

    liniarizare.reserve(1 + LMAX);

    liniarizare.emplace_back(0);
    dfs(1, 1);
    buildAintLCA(1, 1, (int)liniarizare.size() - 1);

    for (int i = 1; i <= n; ++i) /// nu am mai luat alt vector vizitat si pentru constructia path-urilor (tot pentru a castiga memorie)
        vizitat[i] = false;

    paths.reserve(1 + NMAX);
    liniarizarePaths.reserve(1 + NMAX);
    aintLiniarizarePaths.resize(1 + 4 * NMAX);
    liniarizarePaths.emplace_back(-1);
    paths.emplace_back();
    paths.emplace_back();
    paths.back().indexTataPath = -1;
    paths.back().indexTataNodPath = -1;
    paths.back().indexInceput = (int)liniarizarePaths.size() - 1 + 1;
    dfsPath(1);

    buildAintLiniarizarePaths(1, 1, n);

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

        if (tip == 0) /// update
        {
            val[x] = y;
            updateAintLiniarizarePaths(1, 1, (int)liniarizarePaths.size() - 1, indexInLiniarizarePaths[x], y);
        }
        else /// query
        {
            int lca = LCA(x, y);

            out << max(solutiePeLant(x, lca), solutiePeLant(y, lca)) << '\n';
        }
    }

    in.close();
    out.close();

    return 0;
}