Я пытаюсь использовать дерево сегментов для обработки запросов (в O(log N)
на каждый запрос) формы: найти первое значение > x в определенном диапазоне [ql, qr]
(массива размером N
).
Я использовал рекурсивный метод для создания дерева сегментов и его обновления, но мне не удалось получить индекс first value > x
в моем диапазоне.
Вот моя реализация, которая сейчас не работает:
#include <utility>
#include <iostream>
#include <vector>
using namespace std;
vector<int> seg;
vector<int> torre;
int sizeN;
//recursive build of the segtree
long long int build(int i, int l, int r, vector<int> &torri) {
if (r-l==1) return seg[i] = torri[l]; //base case
int mid = (l+r)/2; //split in half
seg[i] = max(build(2*i, l, mid, torri), build((2*i)+1, mid, r, torri));
//return the value of the segment
return seg[i];
}
//recursive update of the segtree
void update(int i, int l, int r, int pos, long long int val) {
if (r-l==1) { seg[i] = val; return; } //base case
int mid = (l+r)/2; //find the middle
if (pos<mid) update(2*i, l, mid, pos, val); //left half
else update(2*i+1, mid, r, pos, val); //right half
seg[i] = max(seg[2*i],seg[2*i+1]); //update with the max
}
int query_destra (int i, int l, int r, int ql, int qr, int x) {
//base case
if (ql >= r or qr <= l) return -1;//no intersection
if (l >= ql and r <= qr) { //included
if (seg[i] <= x) return -1; //if ... the node is not interesting
//return i; <----------- IM MISSING SOMETHING HERE TO MAKE IT RETURN I CORRECTLY
}
int mid = (l+r)/2; //find the center
//prioritizes the first value, if its not interesting, go to the second
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) {
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);
}
int main() {
vector vec{6, 4, 2, 0, 8, 4};
inizializza(vec.size(), vec);
cout << query_destra(1, 0, sizeN, 1, 5, 4) << '\n';
// should produce 4, the index of the first value larger than 4 in the subarray from index 1 to 5 (exclusive), but gives -1
}
Мне удалось получить самое высокое значение в диапазоне, но не первое, поэтому я попробовал другой подход: я попробовал просто поставить «return i
» внутри включенных случаев, но, судя по результатам, это было ОЧЕНЬ неправильно, и не установка где-нибудь «return i
» просто возвращает -1 каждый раз.
После неудачи самостоятельно я попытался следовать тому, что делали другие люди, но я не только не смог понять большую часть кода, который они использовали, но и он не помог мне сделать то, что мне нужно было сделать.
Пример того, каким должен быть результат: при начальном массиве [6, 4, 2, 0, 8, 4]
, x = 4
и моем диапазоне от позиции 1 до позиции 5 моя программа должна иметь возможность возвращать 4 (позиция 8, первое число выше, чем стартовый).
Мне нужно сделать это для числа в массиве как вперед, так и назад.
Я использую С++17.
Любая помощь приветствуется, заранее спасибо.
Я упростил ваш пример, чтобы более наглядно продемонстрировать проблему.
int mid = (l+r)/2; //find the middle
-- Возможно переполнение, если l + r
превышает максимальное значение int
. Вместо этого используйте std::midpoint.
@PaulMcKenzie Забыл упомянуть об этом в основном сообщении, я использую C++17, и эта функция была создана для C++20 или выше.
Ваше решение почти правильное; вы просто упускаете момент, когда вам следует вернуть фактический индекс. Когда вы сузили поиск до конечного узла при спуске по дереву, вы можете напрямую вернуть левый индекс.
Замените эти строки
if (l >= ql and r <= qr) {
if (seg[i] <= x) return -1;
//return i;
}
с
if (seg[i] <= x) return -1;
if (r == l + 1) return l;
Я не думаю, что понимаю это решение. Я попытался поместить ваше решение в свой код, и оно находит, казалось бы, случайное число с различными примерами. Я попробовал этот пример: v[1, 3, 6, 8, 8, 9, 10]
и x=1
, поэтому теперь программа должна найти первый элемент справа выше torre[1] = 3
, но по какой-то причине возвращает 5 (9). Если вместо другого вектора вектор v[1, 7, 6, 8, 8, 9, 10]
и x=1
, он должен вернуть 3 (позиция первых 8), но вместо этого он снова возвращает 5 (позиция 9).
@Tizio Я забыл, что вы использовали исключительное право для своих узлов. Измените строку на if (r == l + 1) return l;
«весь мой код» — требуется редко. Пожалуйста, предоставьте минимально воспроизводимый пример, где вы проделали тяжелую работу по исключению ненужных частей из материала, который мы, пытаясь помочь вам, должны проанализировать.