Untitled

 avatar
unknown
c_cpp
14 days ago
37 kB
5
Indexable
#ifndef CHEATSHEET_H
#define CHEATSHEET_H

#include <algorithm>
#include <iostream>
#include <vector>
#include <queue>
#include <stack>
#include <tuple>
#include <math.h>
#include <functional>

using namespace std;

//=====================================================================================
// 1. bfs, dfs - pe un graf orientat sau neorientat (retinere generica)
// usage example:
// graf g(5, false);
// g.adaugaMuchie(0, 1);
// g.adaugaMuchie(1, 2);
// vector<int> rez_bfs = g.bfs(0);
// vector<int> rez_dfs = g.dfs(0);
//=====================================================================================
class Graf {
public:
    int n;
    bool orientat;
    vector<vector<int>> adj;

    Graf(int n, bool orientat = false) : n(n), orientat(orientat) {
        adj.resize(n);
    }

    void adaugaMuchie(int u, int v) {
        adj[u].push_back(v);
        if(!orientat) {
            adj[v].push_back(u);
        }
    }

    // bfs
    vector<int> bfs(int start) {
        vector<int> viz(n, 0), rez;
        queue<int> q;
        viz[start] = 1;
        q.push(start);
        while(!q.empty()) {
            int node = q.front();
            q.pop();
            rez.push_back(node);
            for(auto &vecin : adj[node]) {
                if(!viz[vecin]) {
                    viz[vecin] = 1;
                    q.push(vecin);
                }
            }
        }
        return rez;
    }

    // dfs (recursiv)
    void dfsUtil(int node, vector<int> &viz, vector<int> &rez) {
        viz[node] = 1;
        rez.push_back(node);
        for(auto &vecin : adj[node]) {
            if(!viz[vecin]) {
                dfsUtil(vecin, viz, rez);
            }
        }
    }

    vector<int> dfs(int start) {
        vector<int> viz(n, 0), rez;
        dfsUtil(start, viz, rez);
        return rez;
    }
};

//=====================================================================================
// 2. noduri critice (articulation points) si muchii critice (bridges)
// usage example (stapane, daca vrei sa afli ce noduri si muchii iti pot da dureri de cap):
// graphcritic gc(5);
// gc.adaugaMuchie(0,1);
// gc.adaugaMuchie(1,2);
// gc.findArticulationPointsAndBridges();
// vector<bool> ap = gc.articulation;
// vector<pair<int,int>> br = gc.bridges;
//=====================================================================================
class GraphCritic {
public:
    int n;
    vector<vector<int>> adj;
    vector<int> disc, low, parent;
    vector<bool> articulation;
    int timp;
    vector<pair<int,int>> bridges;

    GraphCritic(int n) : n(n) {
        adj.resize(n);
        disc.resize(n, -1);
        low.resize(n, -1);
        parent.resize(n, -1);
        articulation.resize(n, false);
        timp = 0;
    }

    void adaugaMuchie(int u, int v) {
        adj[u].push_back(v);
        adj[v].push_back(u);
    }

    void dfsAP(int u) {
        disc[u] = low[u] = timp++;
        int copii = 0;
        for(int v : adj[u]) {
            if(disc[v] == -1) {
                parent[v] = u;
                copii++;
                dfsAP(v);
                low[u] = min(low[u], low[v]);

                if(parent[u] == -1 && copii > 1) {
                    articulation[u] = true;
                }
                if(parent[u] != -1 && low[v] >= disc[u]) {
                    articulation[u] = true;
                }
                if(low[v] > disc[u]) {
                    bridges.push_back({u, v});
                }
            } else if(v != parent[u]) {
                low[u] = min(low[u], disc[v]);
            }
        }
    }

    void findArticulationPointsAndBridges() {
        for(int i = 0; i < n; i++) {
            if(disc[i] == -1) {
                dfsAP(i);
            }
        }
    }
};

//=====================================================================================
// 3. tarjan pentru strongly connected components (graf orientat)
// usage example (ai un graf orientat si vrei scc?):
// tarjanscc t(5);
// t.adaugaMuchie(0,1);
// t.adaugaMuchie(1,2);
// t.buildSCCs();
// vector<vector<int>> componente = t.sccs;
//=====================================================================================
class TarjanSCC {
public:
    int n;
    vector<vector<int>> adj;
    vector<int> disc, low, stiva;
    vector<bool> inStack;
    int timp;
    vector<vector<int>> sccs;

    TarjanSCC(int n) : n(n) {
        adj.resize(n);
        disc.resize(n, -1);
        low.resize(n, -1);
        inStack.resize(n, false);
        timp = 0;
    }

    void adaugaMuchie(int u, int v) {
        adj[u].push_back(v);
    }

    void tarjanDFS(int u) {
        disc[u] = low[u] = timp++;
        stiva.push_back(u);
        inStack[u] = true;

        for(auto &v : adj[u]) {
            if(disc[v] == -1) {
                tarjanDFS(v);
                low[u] = min(low[u], low[v]);
            } else if(inStack[v]) {
                low[u] = min(low[u], disc[v]);
            }
        }

        if(low[u] == disc[u]) {
            vector<int> comp;
            while(true) {
                int node = stiva.back();
                stiva.pop_back();
                inStack[node] = false;
                comp.push_back(node);
                if(node == u) break;
            }
            sccs.push_back(comp);
        }
    }

    void buildSCCs() {
        for(int i = 0; i < n; i++) {
            if(disc[i] == -1) {
                tarjanDFS(i);
            }
        }
    }
};

//=====================================================================================
// 4. verificare bipartit (graf neorientat) - bfs/dfs
// usage example (vrei sa vezi daca graful e bipartit?):
// vector<vector<int>> adj(4);
// adj[0] = {1};
// adj[1] = {0,2};
// bool e_ok = bipartit_check(adj);
//=====================================================================================
bool bipartit_check(const vector<vector<int>> &adj) {
    int n = (int)adj.size();
    vector<int> color(n, -1);

    for(int start = 0; start < n; start++) {
        if(color[start] == -1) {
            queue<int> q;
            color[start] = 0;
            q.push(start);
            while(!q.empty()) {
                int u = q.front();
                q.pop();
                for(auto &v : adj[u]) {
                    if(color[v] == -1) {
                        color[v] = 1 - color[u];
                        q.push(v);
                    } else if(color[v] == color[u]) {
                        return false;
                    }
                }
            }
        }
    }
    return true;
}

//=====================================================================================
// 5. euler path/circuit (hierholzer) - pentru graf neorientat eulerian
// usage example (bai stapane, daca graful tau are toate gradele pare, poti sa faci asa):
// vector<vector<int>> adj(3);
// adj[0] = {1,2};
// adj[1] = {0,2};
// adj[2] = {0,1};
// vector<int> circuit = eulerian_circuit(adj);
//=====================================================================================
vector<int> eulerian_circuit(vector<vector<int>> &adj) {
    vector<int> circuit;
    stack<int> st;
    st.push(0);
    while(!st.empty()) {
        int u = st.top();
        if(!adj[u].empty()) {
            int v = adj[u].back();
            adj[u].pop_back();
            st.push(v);
        } else {
            circuit.push_back(u);
            st.pop();
        }
    }
    return circuit;
}

//=====================================================================================
// 6. edmond-karp (max flow) - o(v*e^2)
// usage example (cand vrei flux maxim, bossule):
// vector<vector<int>> capacity = {{0,10,0},{0,0,5},{0,0,0}};
// int mf = edmond_karp(capacity, 0, 2);
//=====================================================================================
int edmond_karp(vector<vector<int>> capacity, int source, int sink) {
    int n = capacity.size();
    vector<vector<int>> flow(n, vector<int>(n, 0));
    int max_flow = 0;

    while(true) {
        vector<int> parent(n, -1);
        parent[source] = source;
        queue<int> q;
        q.push(source);

        while(!q.empty() && parent[sink] == -1) {
            int u = q.front();
            q.pop();
            for(int v = 0; v < n; v++) {
                if(capacity[u][v] - flow[u][v] > 0 && parent[v] == -1) {
                    parent[v] = u;
                    q.push(v);
                }
            }
        }

        if(parent[sink] == -1) break;

        int augment = INT_MAX;
        int v = sink;
        while(v != source) {
            int u = parent[v];
            augment = min(augment, capacity[u][v] - flow[u][v]);
            v = u;
        }
        v = sink;
        while(v != source) {
            int u = parent[v];
            flow[u][v] += augment;
            flow[v][u] -= augment;
            v = u;
        }
        max_flow += augment;
    }

    return max_flow;
}

//=====================================================================================
// 7. mst: prim, kruskal (pe un graf neorientat cu costuri)
// usage example (n-ai bani multi, deci vrei cost minim?):
// graf_neorientat g(4);
// g.adauga_muchie(0,1,5);
// g.adauga_muchie(1,2,3);
// int cost_prim = g.prim();
// int cost_kruskal = g.kruskal();
//=====================================================================================
class graf_neorientat {
private:
    int n;
    vector<vector<pair<int,int>>> adj;
public:
    graf_neorientat(int n) : n(n) {
        adj.resize(n);
    }

    void adauga_muchie(int u, int v, int cost) {
        adj[u].push_back({v, cost});
        adj[v].push_back({u, cost});
    }

    // prim
    int prim(int start = 0) {
        vector<int> viz(n, 0);
        priority_queue<pair<int,int>, vector<pair<int,int>>, greater<pair<int,int>>> pq;
        pq.push({0, start});
        int total_cost = 0;

        while(!pq.empty()) {
            auto [c, node] = pq.top();
            pq.pop();
            if(viz[node]) continue;
            viz[node] = 1;
            total_cost += c;
            for(auto &ed : adj[node]) {
                if(!viz[ed.first]) {
                    pq.push({ed.second, ed.first});
                }
            }
        }
        return total_cost;
    }

    // kruskal
    struct dsu {
        vector<int> par, rnk;
        dsu(int n) : par(n), rnk(n, 0) {
            for(int i = 0; i < n; i++) par[i] = i;
        }
        int find(int x) {
            if(par[x] == x) return x;
            return par[x] = find(par[x]);
        }
        bool unite(int a, int b) {
            a = find(a);
            b = find(b);
            if(a == b) return false;
            if(rnk[a] < rnk[b]) swap(a, b);
            par[b] = a;
            if(rnk[a] == rnk[b]) rnk[a]++;
            return true;
        }
    };

    int kruskal() {
        vector<tuple<int,int,int>> edges;
        for(int u = 0; u < n; u++) {
            for(auto &p : adj[u]) {
                int v = p.first;
                int c = p.second;
                if(u < v) {
                    edges.push_back({c, u, v});
                }
            }
        }
        sort(edges.begin(), edges.end(),
            [](auto &a, auto &b) { return get<0>(a) < get<0>(b); });
        dsu d(n);
        int total_cost = 0;
        for(auto &e : edges) {
            int c, u, v;
            tie(c, u, v) = e;
            if(d.unite(u, v)) {
                total_cost += c;
            }
        }
        return total_cost;
    }

    vector<vector<pair<int,int>>> getAdj() {
        return adj;
    }

    // cate varfuri (minim) sunt intre nodurile nod1 si nod2
    // usage example (daca vrei distanta in numar de noduri intre 2 capete):
    // unsigned dist = g.distanta_dintre_noduri(0,2);
    unsigned distanta_dintre_noduri(unsigned nod1, unsigned nod2) {
        unsigned distanta = 0;
        vector<int> viz(n, 0);
        queue<pair<int, int>> q;
        q.push({nod1, 0});
        viz[nod1] = 1;
        while(!q.empty()) {
            auto [nod, dist] = q.front();
            q.pop();
            if(nod == (int)nod2) {
                distanta = dist;
                break;
            }
            for(auto &vecin : adj[nod]) {
                if(!viz[vecin.first]) {
                    viz[vecin.first] = 1;
                    q.push({vecin.first, dist + 1});
                }
            }
        }
        return distanta;
    }
};

//=====================================================================================
// 8. dijkstra - o(e log v)
// usage example (te dai barosan si vrei cea mai scurta distanta dintr-un nod spre rest?):
// vector<vector<pair<int,int>>> adj(5);
// adj[0].push_back({1,10});
// vector<int> distante = dijkstra(adj, 0);
//=====================================================================================
vector<int> dijkstra(const vector<vector<pair<int,int>>> &adj, int start) {
    int n = adj.size();
    vector<int> dist(n, INT_MAX);
    dist[start] = 0;
    priority_queue<pair<int,int>, vector<pair<int,int>>, greater<pair<int,int>>> pq;
    pq.push({0, start});

    while(!pq.empty()) {
        auto [cd, node] = pq.top();
        pq.pop();
        if(cd > dist[node]) continue;
        for(auto &edge : adj[node]) {
            int vecin = edge.first;
            int cost = edge.second;
            if(dist[node] + cost < dist[vecin]) {
                dist[vecin] = dist[node] + cost;
                pq.push({dist[vecin], vecin});
            }
        }
    }
    return dist;
}

//=====================================================================================
// 9. bellman-ford - o(v*e)
// usage example (cand ai costuri negative, da' nu cicluri negative):
// vector<tuple<int,int,int>> edges;
// edges.push_back({0,1,5});
// vector<int> d = bellman_ford(5, 0, edges);
//=====================================================================================
vector<int> bellman_ford(int n, int start, vector<tuple<int,int,int>> &edges) {
    vector<int> dist(n, INT_MAX);
    dist[start] = 0;
    for(int i = 0; i < n - 1; i++) {
        for(auto &e : edges) {
            int u, v, c;
            tie(u, v, c) = e;
            if(dist[u] != INT_MAX && dist[u] + c < dist[v]) {
                dist[v] = dist[u] + c;
            }
        }
    }
    return dist;
}

//=====================================================================================
// 10. floyd-warshall - o(v^3)
// usage example (vrei distantele intre toate perechile, boss):
// vector<vector<int>> mat = {{0,5,9999},{9999,0,2},{9999,9999,0}};
// floyd_warshall(mat);
//=====================================================================================
void floyd_warshall(vector<vector<int>> &mat) {
    int n = mat.size();
    for(int k = 0; k < n; k++) {
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < n; j++) {
                if(mat[i][k] < INT_MAX && mat[k][j] < INT_MAX) {
                    mat[i][j] = min(mat[i][j], mat[i][k] + mat[k][j]);
                }
            }
        }
    }
}

//=====================================================================================
// 11. levenshtein distance (edit distance)
// usage example (vrei sa vezi cate operatii difera doua stringuri):
// int d = levenshtein_distance("bai","ratoi");
//=====================================================================================
int levenshtein_distance(const string &s1, const string &s2) {
    int n = s1.size(), m = s2.size();
    vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0));
    for(int i = 0; i <= n; i++) dp[i][0] = i;
    for(int j = 0; j <= m; j++) dp[0][j] = j;

    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= m; j++) {
            if(s1[i - 1] == s2[j - 1]) {
                dp[i][j] = dp[i - 1][j - 1];
            } else {
                dp[i][j] = 1 + min({dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]});
            }
        }
    }
    return dp[n][m];
}

//=====================================================================================
// 12. sortari (mergesort, quicksort) si cautare binara
// usage example (cand vrei sa fii rapid si ordonat, stapane):
// vector<int> arr = {3,1,2};
// merge_sort(arr, 0, arr.size()-1);
// int pos = binary_search_custom(arr, 2);
//=====================================================================================
void merge_sort(vector<int> &arr, int left, int right) {
    if(left >= right) return;
    int mid = (left + right) / 2;
    merge_sort(arr, left, mid);
    merge_sort(arr, mid + 1, right);

    vector<int> temp;
    int i = left, j = mid + 1;
    while(i <= mid && j <= right) {
        if(arr[i] < arr[j]) temp.push_back(arr[i++]);
        else temp.push_back(arr[j++]);
    }
    while(i <= mid) temp.push_back(arr[i++]);
    while(j <= right) temp.push_back(arr[j++]);

    for(int k = left; k <= right; k++) {
        arr[k] = temp[k - left];
    }
}

int partition_qs(vector<int> &arr, int low, int high) {
    int pivot = arr[high];
    int i = low - 1;
    for(int j = low; j < high; j++) {
        if(arr[j] < pivot) {
            i++;
            swap(arr[i], arr[j]);
        }
    }
    swap(arr[i + 1], arr[high]);
    return i + 1;
}

void quicksort(vector<int> &arr, int low, int high) {
    if(low < high) {
        int pi = partition_qs(arr, low, high);
        quicksort(arr, low, pi - 1);
        quicksort(arr, pi + 1, high);
    }
}

int binary_search_custom(const vector<int> &arr, int val) {
    int left = 0, right = (int)arr.size() - 1;
    while(left <= right) {
        int mid = (left + right) / 2;
        if(arr[mid] == val) return mid;
        else if(arr[mid] < val) left = mid + 1;
        else right = mid - 1;
    }
    return -1;
}

//=====================================================================================
// 13. verificare graf hamiltonian
// usage example (vrei sa vezi daca exista un drum care trece prin toate nodurile):
// vector<vector<int>> adj = {{0,1},{1,0}};
// bool e_hamil = hasHamiltonianPath(adj);
//=====================================================================================
bool hasHamiltonianPath(const vector<vector<int>> &adj) {
    int n = (int)adj.size();
    vector<vector<bool>> dp(1 << n, vector<bool>(n, false));

    for(int v = 0; v < n; v++){
        dp[1 << v][v] = true;
    }

    for(int mask = 0; mask < (1 << n); mask++){
        for(int v = 0; v < n; v++){
            if(!dp[mask][v]) continue;
            for(int next = 0; next < n; next++){
                if(!(mask & (1 << next)) && adj[v][next] == 1) {
                    int nextMask = mask | (1 << next);
                    dp[nextMask][next] = true;
                }
            }
        }
    }

    int fullMask = (1 << n) - 1;
    for(int v = 0; v < n; v++){
        if(dp[fullMask][v]) {
            return true;
        }
    }
    return false;
}

//=====================================================================================
// 14. verificare daca e arbore (graf neorientat conex fara cicluri)
// usage example (bagi un graf si vrei sa stii daca e copac?):
// vector<vector<int>> adj(3);
// adj[0] = {1};
// adj[1] = {0,2};
// bool e_arb = isTree(adj);
//=====================================================================================
bool isTree(const vector<vector<int>> &adj) {
    int n = (int)adj.size();
    vector<int> viz(n, 0);

    function<bool(int,int)> dfs = [&](int node, int parent) {
        viz[node] = 1;
        for(auto &vecin : adj[node]) {
            if(viz[vecin] == 0) {
                if(dfs(vecin, node) == false) return false;
            } else if(vecin != parent) {
                return false;
            }
        }
        return true;
    };

    if(dfs(0, -1) == false) return false;
    for(int i = 0; i < n; i++){
        if(viz[i] == 0) return false;
    }
    return true;
}

//=====================================================================================
// 15. lowest common ancestor (lca)
// usage example (cauti stramosul comun, stapane?):
// vector<vector<int>> adj(4);
// adj[0].push_back(1);
// lca l(4, adj, 0);
// int ancestor = l.get_lca(1,2);
//=====================================================================================
class LCA {
public:
    int n, LOG;
    vector<vector<int>> up;
    vector<int> depth;

    LCA(int n, vector<vector<int>> &adj, int root = 0) : n(n) {
        LOG = log2(n) + 1;
        up.assign(n, vector<int>(LOG, -1));
        depth.assign(n, 0);
        dfs(root, -1, adj);
    }

    void dfs(int node, int parent, vector<vector<int>> &adj) {
        up[node][0] = parent;
        for(int i = 1; i < LOG; i++) {
            if(up[node][i - 1] != -1)
                up[node][i] = up[up[node][i - 1]][i - 1];
        }
        for(int v : adj[node]) {
            if(v != parent) {
                depth[v] = depth[node] + 1;
                dfs(v, node, adj);
            }
        }
    }

    int get_lca(int u, int v) {
        if(depth[u] < depth[v]) swap(u, v);
        for(int i = LOG - 1; i >= 0; i--) {
            if(up[u][i] != -1 && depth[up[u][i]] >= depth[v])
                u = up[u][i];
        }
        if(u == v) return u;
        for(int i = LOG - 1; i >= 0; i--) {
            if(up[u][i] != up[v][i]) {
                u = up[u][i];
                v = up[v][i];
            }
        }
        return up[u][0];
    }
};

//=====================================================================================
// 16. centroid decomposition
// usage example (cand vrei sa faci chestii avansate pe arbore, bosule):
// vector<vector<int>> adj(5);
// centroiddecomposition cd(5, adj);
//=====================================================================================
class CentroidDecomposition {
public:
    int n;
    vector<vector<int>> adj, tree;
    vector<int> subtree;
    vector<bool> removed;

    CentroidDecomposition(int n, vector<vector<int>> &adj) : n(n), adj(adj) {
        tree.resize(n);
        removed.assign(n, false);
        subtree.assign(n, 0);
        build(0, -1);
    }

    int find_centroid(int node, int parent, int size) {
        for(int v : adj[node]) {
            if(v != parent && !removed[v] && subtree[v] > size / 2)
                return find_centroid(v, node, size);
        }
        return node;
    }

    void calc_subtree(int node, int parent) {
        subtree[node] = 1;
        for(int v : adj[node]) {
            if(v != parent && !removed[v]) {
                calc_subtree(v, node);
                subtree[node] += subtree[v];
            }
        }
    }

    void build(int node, int parent) {
        calc_subtree(node, -1);
        int centroid = find_centroid(node, -1, subtree[node]);
        removed[centroid] = true;
        if(parent != -1) tree[parent].push_back(centroid);
        for(int v : adj[centroid]) {
            if(!removed[v]) build(v, centroid);
        }
        removed[centroid] = false;
    }
};

//=====================================================================================
// 17. heavy-light decomposition (hld) - implementare completa
// usage example (daca vrei query de tip sum pe un arbore, de exemplu):
// heavylightdecomposition hld(n);
// - initializezi cu add edge
// hld.init(0); // root = 0
// hld.build_segment_tree();
// hld.update(u, val); // update un nod
// int ans = hld.query(u,v); // query pe drumul dintre u si v
//=====================================================================================
struct segtree_hld {
    int n;
    vector<int> st;
    segtree_hld(int n) : n(n) {
        st.resize(4*n, 0);
    }
    void build(vector<int> &vals, int idx, int left, int right) {
        if(left == right) {
            st[idx] = vals[left];
            return;
        }
        int mid = (left+right)/2;
        build(vals, idx*2, left, mid);
        build(vals, idx*2+1, mid+1, right);
        st[idx] = st[idx*2] + st[idx*2+1];
    }
    void update(int idx, int left, int right, int pos, int val) {
        if(left == right) {
            st[idx] = val;
            return;
        }
        int mid = (left+right)/2;
        if(pos <= mid) update(idx*2,left,mid,pos,val);
        else update(idx*2+1,mid+1,right,pos,val);
        st[idx] = st[idx*2] + st[idx*2+1];
    }
    int query(int idx, int left, int right, int ql, int qr) {
        if(ql>qr || ql>right || qr<left) return 0;
        if(ql<=left && right<=qr) return st[idx];
        int mid=(left+right)/2;
        return query(idx*2,left,mid,ql,qr) + query(idx*2+1,mid+1,right,ql,qr);
    }
};

class HeavyLightDecomposition {
public:
    int n;
    vector<vector<int>> adj;
    vector<int> parent, depth, heavy, size, head, pos;
    vector<int> base_array; 
    segtree_hld *seg; 
    int curPos;

    HeavyLightDecomposition(int n) : n(n) {
        adj.resize(n);
        parent.resize(n);
        depth.resize(n);
        heavy.resize(n, -1);
        size.resize(n, 0);
        head.resize(n);
        pos.resize(n);
        curPos = 0;
    }

    void add_edge(int u, int v) {
        adj[u].push_back(v);
        adj[v].push_back(u);
    }

    int dfs(int u, int p) {
        parent[u] = p;
        size[u] = 1;
        int mx=-1;
        for(auto &v : adj[u]) {
            if(v == p) continue;
            depth[v] = depth[u] + 1;
            int sz = dfs(v,u);
            if(sz>mx) {
                mx=sz;
                heavy[u] = v;
            }
            size[u]+=sz;
        }
        return size[u];
    }

    void decompose(int u, int h) {
        head[u] = h;
        pos[u] = curPos++;
        base_array[pos[u]] = 0; // if you want to store initial node values, place them here
        if(heavy[u] != -1) {
            decompose(heavy[u], h);
        }
        for(auto &v : adj[u]) {
            if(v==parent[u] || v==heavy[u]) continue;
            decompose(v,v);
        }
    }

    void init(int root) {
        dfs(root,-1);
        base_array.resize(n,0);
        decompose(root,root);
    }

    void build_segment_tree() {
        seg = new segtree_hld(n);
        seg->build(base_array,1,0,n-1);
    }

    void update(int u, int val) {
        int idx = pos[u];
        seg->update(1,0,n-1,idx,val);
    }

    int query_path(int u, int v) {
        int res = 0;
        while(head[u] != head[v]) {
            if(depth[head[u]] > depth[head[v]]) swap(u,v);
            res += seg->query(1,0,n-1,pos[head[v]], pos[v]);
            v = parent[head[v]];
        }
        if(depth[u] > depth[v]) swap(u,v);
        res += seg->query(1,0,n-1,pos[u],pos[v]);
        return res;
    }

    int query(int u, int v) {
        return query_path(u,v);
    }
};

//=====================================================================================
// 18. fenwick tree (bit)
// usage example (vrei update si query rapid pe sume?):
// fenwicktree ft(10);
// ft.update(1,5);
// int s = ft.range_sum(1,3);
//=====================================================================================
class FenwickTree {
    vector<int> bit;
    int n;
public:
    FenwickTree(int n) : n(n) {
        bit.assign(n + 1, 0);
    }

    void update(int idx, int delta) {
        for(; idx <= n; idx += idx & -idx)
            bit[idx] += delta;
    }

    int sum(int idx) {
        int s = 0;
        for(; idx > 0; idx -= idx & -idx)
            s += bit[idx];
        return s;
    }

    int range_sum(int l, int r) {
        return sum(r) - sum(l - 1);
    }
};

//=====================================================================================
// 19. segment tree (completa)
// usage example (cand ai intervale si vrei query/update smecher, stapane):
// segmenttree st(n);
// st.build(arr,1,0,n-1);
// st.update(1,0,n-1,idx,val);
// int rez = st.query(1,0,n-1,l,r);
//=====================================================================================
class SegmentTree {
public:
    int n;
    vector<int> tree;
    SegmentTree(int n) : n(n) {
        tree.resize(4*n, 0);
    }

    void build(vector<int> &arr, int idx, int start, int end) {
        if(start == end) {
            tree[idx] = arr[start];
            return;
        }
        int mid = (start+end)/2;
        build(arr, idx*2, start, mid);
        build(arr, idx*2+1, mid+1, end);
        tree[idx] = tree[idx*2] + tree[idx*2+1];
    }

    void update(int idx, int start, int end, int pos, int val) {
        if(start == end) {
            tree[idx] = val;
            return;
        }
        int mid = (start+end)/2;
        if(pos <= mid) update(idx*2, start, mid, pos, val);
        else update(idx*2+1, mid+1, end, pos, val);
        tree[idx] = tree[idx*2] + tree[idx*2+1];
    }

    int query(int idx, int start, int end, int left, int right) {
        if(left>right || left> end || right<start) return 0;
        if(left<=start && end<=right) {
            return tree[idx];
        }
        int mid = (start+end)/2;
        int q1 = query(idx*2, start, mid, left, right);
        int q2 = query(idx*2+1, mid+1, end, left, right);
        return q1 + q2;
    }
};

//=====================================================================================
// 20. treap / avl tree / splay tree - exemple minimaliste
// usage example (cand vrei structuri de date mai avansate, stapane):
// ai trei clase: treap, avl, splay. le folosesti cum vrei tu, boss
//=====================================================================================

//-------------------------------------------------------------------------------------
// treap implementation (inserare + cautare)
//-------------------------------------------------------------------------------------
struct TreapNode {
    int key, priority;
    TreapNode* left;
    TreapNode* right;
    TreapNode(int key) : key(key), priority(rand()), left(nullptr), right(nullptr) {}
};

class Treap {
public:
    TreapNode* root;
    Treap() : root(nullptr) {}

    TreapNode* rotateRight(TreapNode* y) {
        TreapNode* x = y->left;
        TreapNode* t2 = x->right;
        x->right = y;
        y->left = t2;
        return x;
    }

    TreapNode* rotateLeft(TreapNode* x) {
        TreapNode* y = x->right;
        TreapNode* t2 = y->left;
        y->left = x;
        x->right = t2;
        return y;
    }

    TreapNode* insertNode(TreapNode* node, int key) {
        if(!node) return new TreapNode(key);
        if(key < node->key) {
            node->left = insertNode(node->left, key);
            if(node->left->priority > node->priority) {
                node = rotateRight(node);
            }
        } else {
            node->right = insertNode(node->right, key);
            if(node->right->priority > node->priority) {
                node = rotateLeft(node);
            }
        }
        return node;
    }

    void insertKey(int key) {
        root = insertNode(root, key);
    }

    bool searchNode(TreapNode* node, int key) {
        if(!node) return false;
        if(node->key == key) return true;
        if(key < node->key) return searchNode(node->left, key);
        return searchNode(node->right, key);
    }

    bool searchKey(int key) {
        return searchNode(root, key);
    }
};

//-------------------------------------------------------------------------------------
// avl tree implementation (inserare + cautare)
//-------------------------------------------------------------------------------------
struct AVLNode {
    int key;
    AVLNode* left;
    AVLNode* right;
    int height;
    AVLNode(int key) : key(key), left(nullptr), right(nullptr), height(1) {}
};

class AVL {
public:
    AVLNode* root;
    AVL() : root(nullptr) {}

    int getHeight(AVLNode* node) {
        return (node ? node->height : 0);
    }

    int getBalance(AVLNode* node) {
        if(!node) return 0;
        return getHeight(node->left) - getHeight(node->right);
    }

    AVLNode* rotateRight(AVLNode* y) {
        AVLNode* x = y->left;
        AVLNode* t2 = x->right;
        x->right = y;
        y->left = t2;
        y->height = max(getHeight(y->left), getHeight(y->right)) + 1;
        x->height = max(getHeight(x->left), getHeight(x->right)) + 1;
        return x;
    }

    AVLNode* rotateLeft(AVLNode* x) {
        AVLNode* y = x->right;
        AVLNode* t2 = y->left;
        y->left = x;
        x->right = t2;
        x->height = max(getHeight(x->left), getHeight(x->right)) + 1;
        y->height = max(getHeight(y->left), getHeight(y->right)) + 1;
        return y;
    }

    AVLNode* insertNode(AVLNode* node, int key) {
        if(!node) return new AVLNode(key);
        if(key < node->key) node->left = insertNode(node->left, key);
        else node->right = insertNode(node->right, key);

        node->height = 1 + max(getHeight(node->left), getHeight(node->right));
        int balance = getBalance(node);

        // left left
        if(balance > 1 && key < node->left->key) {
            return rotateRight(node);
        }
        // right right
        if(balance < -1 && key > node->right->key) {
            return rotateLeft(node);
        }
        // left right
        if(balance > 1 && key > node->left->key) {
            node->left = rotateLeft(node->left);
            return rotateRight(node);
        }
        // right left
        if(balance < -1 && key < node->right->key) {
            node->right = rotateRight(node->right);
            return rotateLeft(node);
        }
        return node;
    }

    void insertKey(int key) {
        root = insertNode(root, key);
    }

    bool searchNode(AVLNode* node, int key) {
        if(!node) return false;
        if(node->key == key) return true;
        if(key < node->key) return searchNode(node->left, key);
        else return searchNode(node->right, key);
    }

    bool searchKey(int key) {
        return searchNode(root, key);
    }
};

//-------------------------------------------------------------------------------------
// splay tree implementation (inserare + cautare + splay)
//-------------------------------------------------------------------------------------
struct SplayNode {
    int key;
    SplayNode* left;
    SplayNode* right;
    SplayNode(int key) : key(key), left(nullptr), right(nullptr) {}
};

class SplayTree {
public:
    SplayNode* root;
    SplayTree() : root(nullptr) {}

    SplayNode* rightRotate(SplayNode* x) {
        SplayNode* y = x->left;
        x->left = y->right;
        y->right = x;
        return y;
    }

    SplayNode* leftRotate(SplayNode* x) {
        SplayNode* y = x->right;
        x->right = y->left;
        y->left = x;
        return y;
    }

    // splay operation: bring key to root
    SplayNode* splay(SplayNode* root, int key) {
        if(!root || root->key == key) return root;

        // key < root->key
        if(key < root->key) {
            if(!root->left) return root;
            // zig-zig
            if(key < root->left->key) {
                root->left->left = splay(root->left->left, key);
                root = rightRotate(root);
            }
            // zig-zag
            else if(key > root->left->key) {
                root->left->right = splay(root->left->right, key);
                if(root->left->right) {
                    root->left = leftRotate(root->left);
                }
            }
            return (!root->left) ? root : rightRotate(root);
        }
        else {
            if(!root->right) return root;
            // zig-zig
            if(key > root->right->key) {
                root->right->right = splay(root->right->right, key);
                root = leftRotate(root);
            }
            // zig-zag
            else if(key < root->right->key) {
                root->right->left = splay(root->right->left, key);
                if(root->right->left) {
                    root->right = rightRotate(root->right);
                }
            }
            return (!root->right) ? root : leftRotate(root);
        }
    }

    SplayNode* insertNode(SplayNode* root, int key) {
        if(!root) return new SplayNode(key);
        root = splay(root, key);
        if(root->key == key) return root; 
        SplayNode* newNode = new SplayNode(key);
        if(key < root->key) {
            newNode->right = root;
            newNode->left = root->left;
            root->left = nullptr;
        } else {
            newNode->left = root;
            newNode->right = root->right;
            root->right = nullptr;
        }
        return newNode;
    }

    void insertKey(int key) {
        root = insertNode(root, key);
    }

    bool searchKey(int key) {
        root = splay(root, key);
        return (root && root->key == key);
    }
};

#endif // CHEATSHEET_H

// exemplu minimal in main, daca vrei sa testezi ceva:
int main() {


}
Leave a Comment