Árbol de segmentos: plantillas y operaciones comunes

Introducción al árbol de segmentos

#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 100010;

int n, q;
ll arr[MAXN];
ll segtree[MAXN << 2];
ll lazy[MAXN << 2];

void pushUp(int nodo) {
    segtree[nodo] = segtree[nodo << 1] + segtree[(nodo << 1) | 1];
}

void construir(int l, int r, int nodo) {
    lazy[nodo] = 0;
    if (l == r) {
        segtree[nodo] = arr[l];
        return;
    }
    int mid = (l + r) >> 1;
    construir(l, mid, nodo << 1);
    construir(mid + 1, r, (nodo << 1) | 1);
    pushUp(nodo);
}

void aplicarLazy(int nodo, int longitud) {
    if (lazy[nodo] != 0) {
        int izq = nodo << 1;
        int der = (nodo << 1) | 1;
        lazy[izq] += lazy[nodo];
        lazy[der] += lazy[nodo];
        segtree[izq] += lazy[nodo] * (longitud - (longitud >> 1));
        segtree[der] += lazy[nodo] * (longitud >> 1);
        lazy[nodo] = 0;
    }
}

void actualizar(int ql, int qr, ll valor, int l, int r, int nodo) {
    if (ql <= l && r <= qr) {
        lazy[nodo] += valor;
        segtree[nodo] += valor * (r - l + 1);
        return;
    }
    aplicarLazy(nodo, r - l + 1);
    int mid = (l + r) >> 1;
    if (ql <= mid) actualizar(ql, qr, valor, l, mid, nodo << 1);
    if (qr > mid) actualizar(ql, qr, valor, mid + 1, r, (nodo << 1) | 1);
    pushUp(nodo);
}

ll consultar(int ql, int qr, int l, int r, int nodo) {
    if (ql <= l && r <= qr) {
        return segtree[nodo];
    }
    aplicarLazy(nodo, r - l + 1);
    int mid = (l + r) >> 1;
    ll res = 0;
    if (ql <= mid) res += consultar(ql, qr, l, mid, nodo << 1);
    if (qr > mid) res += consultar(ql, qr, mid + 1, r, (nodo << 1) | 1);
    return res;
}

int main() {
    scanf("%d %d", &n, &q);
    for (int i = 1; i <= n; i++) scanf("%lld", &arr[i]);
    construir(1, n, 1);
    while (q--) {
        int tipo, x, y;
        ll z;
        scanf("%d", &tipo);
        if (tipo == 1) {
            scanf("%d %d %lld", &x, &y, &z);
            actualizar(x, y, z, 1, n, 1);
        } else {
            scanf("%d %d", &x, &y);
            printf("%lld\n", consultar(x, y, 1, n, 1));
        }
    }
    return 0;
}

Búsqueda del valor máximo en un intervalo


Entrada:
8 8
9 3 1 7 5 6 0 8
1 6
1 5
2 7
2 6
1 8
4 8
3 7
1 8

Salida:
9 9 7 7 9 8 7 9

Implementación con arreglos estáticos:

#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN = 100010;

int n, m;
int arr[MAXN];
int seg[MAXN << 2];

void pushUp(int nodo) {
    seg[nodo] = max(seg[nodo << 1], seg[(nodo << 1) | 1]);
}

void construir(int l, int r, int nodo) {
    if (l == r) {
        seg[nodo] = arr[l];
        return;
    }
    int mid = (l + r) >> 1;
    construir(l, mid, nodo << 1);
    construir(mid + 1, r, (nodo << 1) | 1);
    pushUp(nodo);
}

int consultar(int ql, int qr, int l, int r, int nodo) {
    if (ql <= l && r <= qr) {
        return seg[nodo];
    }
    int mid = (l + r) >> 1;
    int res = 0;
    if (ql <= mid) res = consultar(ql, qr, l, mid, nodo << 1);
    if (qr > mid) res = max(res, consultar(ql, qr, mid + 1, r, (nodo << 1) | 1));
    return res;
}

int main() {
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= n; i++) scanf("%d", &arr[i]);
    construir(1, n, 1);
    while (m--) {
        int a, b;
        scanf("%d %d", &a, &b);
        printf("%d\n", consultar(a, b, 1, n, 1));
    }
    return 0;
}

Versión con punteros (evita desbordamiento de memoria):

#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN = 100010;

struct Nodo {
    int l, r;
    int maximo;
    Nodo *izq, *der;
    Nodo() : l(0), r(0), maximo(0), izq(nullptr), der(nullptr) {}
};

int arr[MAXN];
int n, m;

void pushUp(Nodo *nodo) {
    nodo->maximo = max(nodo->izq->maximo, nodo->der->maximo);
}

void construir(int l, int r, Nodo *nodo) {
    nodo->l = l;
    nodo->r = r;
    if (l == r) {
        nodo->maximo = arr[l];
        return;
    }
    int mid = (l + r) >> 1;
    if (!nodo->izq) nodo->izq = new Nodo();
    if (!nodo->der) nodo->der = new Nodo();
    construir(l, mid, nodo->izq);
    construir(mid + 1, r, nodo->der);
    pushUp(nodo);
}

int consultar(Nodo *nodo, int ql, int qr) {
    if (!nodo) return 0;
    if (ql <= nodo->l && nodo->r <= qr) {
        return nodo->maximo;
    }
    int mid = (nodo->l + nodo->r) >> 1;
    int res = 0;
    if (ql <= mid) res = consultar(nodo->izq, ql, qr);
    if (qr > mid) res = max(res, consultar(nodo->der, ql, qr));
    return res;
}

int main() {
    Nodo *raiz = new Nodo();
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= n; i++) scanf("%d", &arr[i]);
    construir(1, n, raiz);
    while (m--) {
        int a, b;
        scanf("%d %d", &a, &b);
        printf("%d\n", consultar(raiz, a, b));
    }
    return 0;
}

La implementación con punteros requiere cuidado al escribir, pero evita el riesgo de desbordamiento de memoria (MLE) inherente a los arreglos estáticos de gran tamaño.

Etiquetas: Árbol de Segmentos lazy propagation consulta por rango actualización por rango C++

Publicado el 7-13 17:53