Segment tree per muraglia

Ciao, sto cercando di risolvere muraglia con un segment tree e, non avendo mai usato questa struttura, ho letto i vari argomenti sul forum su questo problema. Ho quindi implementato il segment basandomi su questo e ho aggiunto la funzione get_first da qui. Posto che devo ancora riscrivere il tutto per la sottoposizione, nemmeno copiando e incollando get_first riesco a ottenere i risultati giusti :smiling_face_with_tear:.

#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>

using namespace std;

typedef long long ll;

struct segTree{
    int N;
    vector<ll> st;
    
    segTree(vector<ll> &A) { //costruttore 
        N = A.size();
        st.resize (4*N); 
        build(1,0,N,A); //chiamo la funzione build per inizializzare il segment tree
    } 
    
    void call_update(int pos, ll newval){ //funzione per aggiornare il segment tree
        update(1, 0, N, pos, newval);
    }
    
    
    
    int call_get_first(int pos, int val){
        return get_first(1, 0, N-1, pos+1, N, val); //non sono sicuro di che valori passare in input
    }
    
    int call_get_last(int pos, int val){
       return get_first(1, 0, N-1, 0, pos-1, val);
    }
    
    
    
    
    
    ll build (int i, int left, int right, vector<ll> &A) { // non da problemi
    
        if (right - left == 1) { 
            return st[i] = A[left];
        }

        int mid = (left + right) / 2; 
        st[i] = max(build(2*i, left, mid, A), build(2*i+1, mid, right, A)); 
        
        return st[i];
    } 
    
    
    void update (int i, int left, int right, int pos, ll val) { // non da problemi

        if (right - left == 1) {
            st[i] = val;
            return;
        }
    
    
        int mid = (left + right) / 2;
    
        if (pos < mid) { 
            update (2*i, left, mid, pos, val);
        } 
        
        else { 
            update (2*i+1, mid, right, pos, val);
        }
    
        st[i] = max(st[2*i],st[2*i+1]);
    }
    

    
    

    int get_first(int v, int lv, int rv, int l, int r, int x) {
        if(lv > r || rv < l) return -1; // primo caso base: se il segment del nodo in cui sono è 
                                        // totalmente esterno al range [l..r] della query, allora non mi interessa 

            if(l <= lv && rv <= r) {    // secondo caso base: se il nodo in cui sono è totalmente interno al range [l...r] distinguo due casi:
                                        
                if(st[v] <= x) return -1; // 2.1) se non posso trovare un valore "utile" non mi interessa
        
                while(lv != rv) {       //2.2) se c'è un valore "utile" implemento una binary search per trovare quello più a sinistra
                    int mid = lv + (rv-lv)/2;
                    if(st[2*v] > x) {
                        v = 2*v;
                        rv = mid;
                    }else {
                        v = 2*v+1;
                        lv = mid+1;
                    }
                }
                return lv;
            }
            
        //se non ricado nei casi base continuo con la ricorsione
    
        int mid = lv + (rv-lv)/2;       
        int rs = get_first(2*v, lv, mid, l, r, x); //scendo prima nel figlio di sinistra 
        if(rs != -1) return rs; //se il figlio di sinistra contiene un valore "utile" lo restituisco
        return get_first(2*v+1, mid+1, rv, l ,r, x); //altrimenti devo scendere nel destro
    }
    
    
    int get_last(int v, int lv, int rv, int l, int r, int x) { //stessa funzione di get_first invertendo l'ordine con cui si scende nei figli
        if(lv > r || rv < l) return -1;
            if(l <= lv && rv <= r) {
                if(st[v] <= x) return -1;
                
                while(lv != rv) {
                    int mid = lv + (rv-lv)/2;
                    if(st[2*v+1] > x) {
                        v = 2*v+1;
                        lv = mid+1;
                    }else {
                        v = 2*v;
                        rv = mid;
                    }
                }
                return lv;
            }
    
        int mid = lv + (rv-lv)/2;
        int ls = get_last(2*v+1, mid+1, rv, l, r, x);
        if(ls != -1) return ls;
        return get_last(2*v, lv, mid, l ,r, x);
    }
    
   
   
    
};



int main()
{
    vector<long long> A = {7, 4, 78, 2, 6, 4, 1, 9, 10, 7, 100, 56, 8, 1};
    
    segTree maxst(A);
    
    
    int a = 5;
    
    cout <<  maxst.call_get_last(a, A[a-1]) <<" "<< maxst.call_get_first(a, A[a-1]) << endl; //non sono sicuro se passare A[a] (0 based) o A[a-1] (1-based)

    return 0;
}

Immagino che il problema siano i valori passati in input alla funzione, ma anche con svariati tentativi non capisco innanzitutto se debba passare il valore di x 1-based (come il segment) o 0-based (come il vector), inoltre non credo che i valori che passo per lv, rv, l, r siano giusti. Aggiungendo o togliendo un elemento dal vettore A ottengo risultati diversi. :sob:

PS non ho commentato le funzioni build e update perché non mi davano problemi, ma se serve posso aggiungere qualcosa anche per quelle.

Ciao, c’è qualche problema nelle query.
Non ho ben capito come utilizzi la binary search nel caso base. Le funzioni ricorsive sono utili a evitare cicli, quindi dovresti provare a riformulare la ricorsione senza utilizzarli.

Ad esempio, come potresti formulare i casi base? Puoi sfruttare le informazioni sul massimo per sapere se ricorrere su quel range o saltarlo?

Nel caso generico cosa fai se ad esempio stai cercando il primo più grande verso destra? Se cerchi verso sinistra cambia qualcosa?

Lascio sotto una traccia in pseudocodice di come potresti implementare la ricerca verso destra del primo valore maggiore di X.

spoiler
casi base:
  il range è totalmente escluso -> in quel range non c'è la risposta
  il massimo in quel range è <= X -> in quel range non c'è la risposta
  il range è lungo 1 -> hai trovato la risposta, restituisci l'indice dell'estremo sinistro del range
caso generico:
  cerco nella metà sinistra
    se trovo una risposta la restituisco
  altrimenti cerco nella metà destra
    se trovo una risposta la restituisco
  altrimenti non c'è risposta quindi restituisco N

PS: ricontrolla bene la condizione del range totalmente escluso

Update: ho letto l’articolo su Cp-Algorithms e ti sconsiglio di usare quell’approccio, mi sembra che complichi e basta ma comunque se pensi di averlo capito meglio continua con quello

3 Mi Piace

Ciao, grazie mille! Riscrivendo la query con questa traccia ho ottenuto 100/100.

PS grazie mille anche per la preziosa introduzione ai segment.

1 Mi Piace