En ciencias de la computación, un Segment Tree es una estructura de datos (arbol) que puede soportar dos operaciones: procesar consultas sobre rangos y actualizar el valor de un array.
Los segment Trees pueden soportar sumas sobre rangos, mínimos y máximos valores en un rango y otros tipos de consulta en tiempo O(log n).
Un segment tree es un árbol binario tal que los nodos del nivel más inferior (hojas del árbol) corresponde a los elementos del array, y los demás nodos contienen información necesaria para procesar consulta sobre rangos.
El árbol se construye recursivamente.
Consideren el siguiente array:
                Su correspondiente segment tree sería algo como esto (calcula sumas de rangos):
                Encontrar el indice del elemento más pequeño de un array en el rango [i..j]
Tenemos un array de números:
                Construyendo el segment Tree:
                
                
                
                Hay que tener en cuenta 3 situaciones:
                
                
                
                Pero... no solamente funciona con datos estáticos. Veamos que pasa cuando necesitamos actualizar.
                
                
                
                
                
                Ahora vamos con el código :D
Para construir nuestro Segment Tree de manera correcta debemos tener en cuenta que nos interesan las siguientes 3 funciones:
Estructura de nuestra clase Segment Tree
                        
class SegmentTree{
    private int st[], data; // Segment tree y Array de Datos
    private int n; // Tamaño del array de datos
    public SegmentTree(int [] data){...}
    private int left(int p){...}
    private int right(int p){...}
    private void build(int p, int L, int R){...}
    private int update(int p, int L, int R, int idx, int new_value){...}
    private int query(int p, int L, int R, int i, int j){...}
    public void update(int idx, int new_value){...}
    public int query(int i, int j){...}
}
                        
                    
                Desplazamiento por los nodos del arbol
                        
private int left(int L){
    return L << 1;  // * 2
}
private int right(int R){
    return R << 1 | 1; // * 2 + 1
}
                        
                    
                Construyendo el Segment Tree
                        
public SegmentTree(int [] data){
    data = data;
    n = data.lenght();
    st = new int[ 4 * n ];
    build(1, 0, n - 1); //Nodo, inicio, fin
}
private void build(int p, int L, int R){ // * DEPENDE DE LO QUE SE NECESITE
    if( L == R ){
        st[p] = data[L]; // *
    }else{
        int mid = ( L + R ) / 2;
        build( left(p), L, mid );
        build( right(p), mid + 1, R);
        int p1, p2;
        p1 = st[ left(p) ];
        p2 = st[ right(p) ];
        st[p] = ( data[p1] < data[p2] ) ? p1 : p2; // *
    }
}
                        
                    
                Realizar consultas(query) sobre el Segment Tree
                        
public int query(int i, int j){
    return query(1, 0, n-1, i, j);
}
private int query(int p, int L, int R, int i, int j){
    if( i > R || j < L ) return -1; // O un valor que no sea usado
				    // Se sale completamente del rango buscado
    if( L >= i && R <= j ) return st[p];
    int mid = ( L + R ) / 2;
    int p1 = query( left(p), L, mid, i, j);
    int p2 = query( right(p), mid + 1, R, i, j);
    if( p1 == -1 ) return p2;
    if( p2 == -1 ) return p1;
    return ( data[p1] < data[p2] ) ? p1 : p2;
}
                        
                    
                Actualizar el Segment Tree
                        
public void update(int idx, int new_value){
    return update(1, 0, n - 1, idx, new_value);
}
private int update(int p, int L, int R, int idx, int new_value){
    if( idx < L || idx > R ) return st[p]; // Fuera de rango
    if( L == R ){
        data[idx] = new_value;
        return st[p];
    }
    int mid = ( L + R ) / 2;
    int p1 = update( left(p), L, mid, idx, new_value);
    int p2 = update( right(p), mid + 1, R, idx, new_value);
    st[p] = ( data[p1] < data[p2] ) ? p1 : p2;
}
                        
                    
                ¿Cómo usarla?
                        
int[] A = new int[] { 18, 17, 13, 19, 15, 11, 20 };
SegmentTree st = new SegmentTree(A);
st.rmq(1,3);
st.update_point(5,100);
                        
                    
                IMPORTANTE... Recuerden que podemos adaptar la estructura a lo que necesitemos :3
¿Dudas Hasta acá?