Динамические структуры данных

Материал из Algocode wiki
Перейти к: навигация, поиск

Иногда в задачах требуется построить структуру данных на массиве очень большого размера (таком, что он не помещается в память). При этом известно, что количество запросов к структуре невелико. Типичная задача: Дан массив размером $n\ (1 \le n \le 10^9)$, изначально заполненный нулями. Необходимо ответить на $q\ (1 \le q \le 10^5)$ запросов одного из двух типов:

  • Изменить значение элемента.
  • Посчитать сумму на отрезке.

Решение

ЗамечаниеМы умеем решать эту задачу с помощью дерева отрезков, но сталкиваемся с недостатком памяти. Иногда это можно побороть, если воспользоваться техникой сжатия координат. Писать неявное ДО в такой ситуации — стрелять из пушки по воробьям.

Сделаем в дереве отрезков ряд изменений. Во-первых, напишем его на структурах и указателях. Каждая вершина дерева отрезков — это объект, который хранит указатели на своего левого и правого сына. Давайте решать эту задачу так, как мы решали бы ее обычным деревом отрезков, но не вызовем операцию построения. То есть изначально наше дерево отрезков — это корень. Создавать вершины мы будем в операциях обновления, если вершина еще не была создана (похожим на [отложенные операции] образом). При get-запросе считаем, что если вершина не создана, то сумма в ней 0 (логично ведь). Тогда за один запрос мы создадим $O(\log n)$ вершин, суммарно все будет работать за $O(q \log n)$ времени и $O(q \log n)$ памяти.

 1 struct segtree {
 2 	segtree* l = nullptr, r = nullptr;
 3 	int sum = 0;
 4 };
 5 
 6 segtree *root = new segtree();
 7 
 8 segtree * make() {
 9 	return new segtree();
10 }
11 
12 int getsum(segtree* v) {
13 	if (v == nullptr) return 0;
14 	return v->sum;
15 }
16 
17 void update(segtree* v, int tl, int tr, int pos) {
18 	if (tl + 1 == tr) {
19 		v->sum++;
20                 return;
21 	}
22 	if (v->l == nullptr) {
23 		v->l = make();
24 	}
25 	if (v->r == nullptr) {
26 		v->r = make();
27 	}
28 	int tm = (tl + tr) / 2;
29 	if (pos < tm) {
30 	        update(v->l, tl, tm, pos);
31 	}
32 	else {
33 		update(v->r, tm, tr, pos);
34 	}
35 	v->sum = getsum(v->l) + getsum(v->r);
36 	return nw;
37 }
38 
39 inline int get(segtree *v, int tl, int tr, int l, int r) {
40 	if (l >= tr || tl >= r || v == nullptr) {
41                  return 0;
42         }
43         if (l <= tl && tr <= r) {
44                   return v->sum;
45         }
46         int tm = (tl + tr) / 2;
47         return get(v->l, tl, tm, l, r) + get(v->r, tm, tr, l, r);
48 }

Use Cases

Используемая в статье техника полезна при работе с:



Автор конспекта: Константин Амеличев

По всем вопросам пишите в telegram @kik0s [Категория:Структуры данных для запросов на отрезке]