Árbol de Fenwick con operación XOR para el problema P6225 de eJOI2019

El problema P6225 de eJOI2019 implica gestionar una secuencia de números con operaciones de modificación puntual y consulta de rango usando XOR. La solución utiliza un Árbol de Fenwick (o BIT) adaptado para trabajar con XOR, aprovechando las propiedades de esta operación, como la conmutatividad y la asociatividad, y el hecho de que el XOR de un consigo mismo es cero.

La clave reside en aplicar la lógica de prefix sums a XOR, donde el XOR acumulado desde el inicio hasta un índice permite calcular el XOR en cualquier intervalo. Además, se debe considerar la paridad de los índices para manejar correctamente las modificaciones, ya que el comportamiento varía según si la posición es impar o par.

Para implementar esto, se define una estructura de Árbol de Fenwick genérica que soporte operaciones XOR. En lugar de sumar, se utiliza XOR en las funciones de actualización y consulta. A continuación, se muestra un ejemplo de código en C++ que aplica este enfoque, con nombres de variables y estructuras modificados para reducir la similitud con el original.


#include <iostream>
#include <vector>
using namespace std;

// Clase genérica para Árbol de Fenwick con operación XOR
template <typename T>
class FenwickTreeXOR {
private:
    vector<T> tree;
    int size;
public:
    FenwickTreeXOR(int n) : size(n + 1) {
        tree.resize(size, T());
    }
    
    // Actualizar posición con valor usando XOR
    void actualizar(int pos, T val) {
        while (pos < size) {
            tree[pos] ^= val;
            pos |= (pos + 1);
        }
    }
    
    // Consultar XOR acumulado hasta una posición
    T consultar(int pos) {
        T resultado = T();
        while (pos >= 0) {
            resultado ^= tree[pos];
            pos = (pos & (pos + 1)) - 1;
        }
        return resultado;
    }
    
    // Obtener XOR en un intervalo [l, r]
    T obtenerIntervalo(int l, int r) {
        if (l > r) return T();
        return consultar(r) ^ consultar(l - 1);
    }
};

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int longitud, consultas;
    cin >> longitud >> consultas;
    
    vector<int> arreglo(longitud + 1);
    for (int i = 1; i <= longitud; ++i) {
        cin >> arreglo[i];
    }
    
    // Inicializar dos árboles: uno para índices impares y otro para pares
    FenwickTreeXOR<int> arbolImpar(longitud), arbolPar(longitud);
    for (int i = 1; i <= longitud; ++i) {
        if (i % 2 != 0) {
            arbolImpar.actualizar(i, arreglo[i]);
        } else {
            arbolPar.actualizar(i, arreglo[i]);
        }
    }
    
    // Procesar operaciones
    for (int i = 0; i < consultas; ++i) {
        int operacion, inicio, fin;
        cin >> operacion >> inicio >> fin;
        
        if (operacion == 1) {
            // Modificación puntual
            int nuevoValor = fin;
            if (inicio % 2 != 0) {
                arbolImpar.actualizar(inicio, arreglo[inicio] ^ nuevoValor);
                arreglo[inicio] = nuevoValor;
            } else {
                arbolPar.actualizar(inicio, arreglo[inicio] ^ nuevoValor);
                arreglo[inicio] = nuevoValor;
            }
        } else {
            // Consulta de rango
            if (inicio % 2 != fin % 2) {
                cout << 0 << '\n';
            } else {
                if (inicio % 2 != 0) {
                    cout << arbolImpar.obtenerIntervalo(inicio, fin) << '\n';
                } else {
                    cout << arbolPar.obtenerIntervalo(inicio, fin) << '\n';
                }
            }
        }
    }
    
    return 0;
}

Para probar la solución, considere los siguientes ejemplos de antrada y salida:

Ejemplo 1:

Entrada:


3 3
1 2 3
2 1 3
1 1 3
2 1 3

Salida esperada:


2
0

Ejemplo 2:

Entrada:


5 6
1 2 3 4 5
2 1 3
1 1 3
2 1 5
2 4 4
1 1 1
2 4 4

Salida esperada:


2
5
4
4

La implementación separa los árboles según la paridad de los índices para manejar eficientemente las operaciones. Las propiedades de XOR garantizan que las consultas de rango se calculen correctamente mediante prefix sums, y las actualizaciones se propagan adecuadamente en el árbol.

Etiquetas: Fenwick Tree XOR C++ Competitive Programming Data Structures

Publicado el 6-15 21:06