(muraglia) segtree: trovare primo valore > x

Sto cercando di risolvere muraglia da un paio di giorni ma non sto avendo molto successo.
Credo di essere riuscito a comprendere il funzionamento della creazione di un segment tree e anche dell’update, però ancora non sono riuscito a comprendere come trovare il primo valore >x in uno specifico range.

//GRADER IGNORARE-------------------------------------------------------------
#include <utility>
#include <iostream>
#include <vector>
using namespace std;

// Declaring variables
static int R;
static vector<int> risultato1;
static vector<int> risultato2;

// Declaring functions
void inizializza(int N, vector<int> H);

// Functions ad-hoc for this grader
pair<int, int> chiedi(int x);
void cambia(int x, int h);

void leggi_eventi(int M) {
    for (int i = 0; i < M; i++) {
        char tipo;
        cin >> tipo;

        if (tipo == 'Q') {
            int x;
            cin >> x;
            pair<int, int> risultato = chiedi(x);
            risultato1[R] = risultato.first;
            risultato2[R] = risultato.second;
            R++;
        } else {
            int x, h;
            cin >> x >> h;
            cambia(x, h);
        }
    }
}


int main() {

    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
    // Reading input
    int N, M;
    cin >> N >> M;

    vector<int> H(N);
    risultato1.resize(M);
    risultato2.resize(M);

    for (int i = 0; i < N; i++) {
        cin >> H[i];
    }

    // Calling functions
    inizializza(N, H);
    leggi_eventi(M);

    // Writing output
    for (int i = 0; i < R; i++) {
        cout << risultato1[i] << ' ' << risultato2[i] << '\n';
    }

    return 0;
}




    
//RISOLUZIONE----------------------------------------------------------------------------------------------------------------

//SEGMENT TREE
vector<int> seg;
vector<int> torre;
int sizeN;

long long int build(int i, int l, int r, vector<int> &torri) {

    if(r-l==1) return seg[i] = torri[l]; //caso base

    int mid = (l+r)/2; //divido a metà
    //creo i figli, e ricorsivamente risalgo al padre facendo sempre salire il maggiore.
    seg[i] = max(build(2*i, l, mid, torri), build((2*i)+1, mid, r, torri));

    //restituisco il valore del segmento
    return seg[i];
}

void update(int i, int l, int r, int pos, long long int val) {

    if(r-l==1) { seg[i] = val; return; } //caso base

    int mid = (l+r)/2; //trova il centro
    if(pos<mid) update(2*i, l, mid, pos, val); //metà sinistra
    else update(2*i+1, mid, r, pos, val); //metà destra

    seg[i] = max(seg[2*i],seg[2*i+1]); //aggiorno il valore con il massimo tra i due figli
}


int query_destra (int i, int l, int r, int ql, int qr, int x) {

    //casi base
    if (ql >= r or qr <= l) return -1;//nessuna intersezione

    if (l >= ql and r <= qr) { //incluso
        if(seg[i] <= x) return -1; //nel caso in cui il nodo è minore di X non è interessante
        //return i;   <--------------- probabilmente l'errore
    }

    int mid = (l + r) / 2;

    printf("controllo valore:%d nodo:%d x:%d\n", seg[i], i, x);

    //si da la priorità al primo valore, in caso non è un valore interessante, si scende al secondo
    int ans = query_destra(2*i, l, mid, ql, qr, x);
    if(ans != -1) return ans;
    return query_destra(2*i+1, mid, r, ql, qr, x);
}


pair<int, int> chiedi(int x) {

    //stampa il vettore
    for(int i=0; i<sizeN*4; i++) printf("%d ", seg[i]); printf("\n");

    return make_pair(0/*query_sinistra(1,0,sizeN, 0, x, torre[x])*/,query_destra(1, 0, sizeN, x, sizeN, torre[x]));

}

void cambia(int x, int h) {
    update(1, 0, sizeN, x, h);
}

void inizializza(int N, vector<int> H) {
    sizeN = H.size();
    torre.resize(sizeN);
    seg.resize(sizeN*4);
    for(int i=0; i<sizeN; i++) torre[i] = H[i];
    build(1,0,N,H);
}

Riesco a capire che mi manca il modo di far restituire i nella ricorsione (visto che al momento può solo restituire -1) ma non capisco la ragione per cui mettere return i; dopo i casi base non possa funzionare.

Qualsiasi tipo di aiuto è apprezzato, ringrazio in anticipo.

Quando trovi una foglia (leaf) cosa fai?

Quando trovo la foglia devo trovare il modo di verificare che è la prima che mi interessa, e poi fare ritornare l’indice nel caso in cui è maggiore di x, altrimenti si ignora e si passa a un altra foglia

E come fai a sapere se sei arrivato a una foglia nella tua query_destra()?

Quello che farei per capire quando sono arrivato a una foglia sarebbe
if(l == r) return l;
Però facendo cosi mi dà dei risultati completamente errati.

Ho provato questo esempio:
torre[1, 3, 6, 8, 8, 9, 10] con x=1, quindi ora dovrebbe ritornarmi il valore del primo elemento maggiore di torre[1] = 3, ma per qualche ragione decide di ritornare 5 (9).

Se a differenza del primo esempio, il vettore fosse stato torre[1, 7, 6, 8, 8, 9, 10] con x=1, allora mi avrebbe dovuto ritornare 3 (la posizione del primo 8) ma invece mi ritorna 5 di nuovo (la posizione di 9).

Se al posto di 7 ci fosse stato un 9 mi ritorna 6 (la posizione del 10)

Ok, devi fare solo attenzione che nella query stai operando con un intervallo aperto [l,r) e non con un intervallo chiuso [l,r]. E attenzione che oltre ad aggiornare il segment tree devi aggiornare anche le torri.
Con queste 2 correzioni tutto viene al posto giusto.

1 Mi Piace

Ho sistema il problema che mancava l’aggiornamento del valore delle torri, ma non credo di aver capito come sistemare il problema dell’intervallo aperto.

La mia interpretazione del tuo consiglio sarebbe che il problema si trova in questo punto:

int ans = query_destra(2*i, l, mid, ql, qr, x);
if(ans != -1) return ans;
return query_destra(2*i+1, mid, r, ql, qr, x);

e che per qualche ragione non comprende anche il bordo destro?
Ho riletto anche il resto della mia funzione per capire se avevo mancato qualche =, ma in tutti gli if presenti nella funzione c’è un >= o un <=.

Ho anche avuto una seconda interpretazione del consiglio dove io ho l’intervallo chiuso e mi stai dicendo che dovrebbe essere aperto.
In quel caso la mia soluzione sarebbe stata:
if (l >= ql and r <= qr)if (l >= ql and r < qr)
Ma fare questo mi fa terminare il programma con l’errore che mi dice che sto accedendo a della memoria che non si può accedere.

Il tuo segment è già impostato su intervallo aperto[l,r) è solo la foglia della query a non essere corretta,in pratica:
if(r-l == 1) return l;

1 Mi Piace

Alla fine sono riuscito a risolvere grazie al consiglio originale, ora tutta la parte destra funziona correttamente, mi manca solo da renderlo simmetrico per il lato sinistro.
Non so come ringraziarti