Прежде чем читать эту статью, нужно понимать, что такое декартово дерево по неявному ключу (это тема не одной статьи, поэтому об этом лучше почитать тут). Сжатие пространста — метод, используемый для сжатия на отрезке данных. Например, вместо того, чтобы хранить множество {1, 1, 1, 2, 2, 2, 3, 1, 1} будем хранить {1 x 3, 2 x 3, 3 x 1, 1 x 2}.
Теперь попробуем сжимать пространство с помощью такого метода и иметь возможность выполнять онлайн операции с любым отрезком.
Сразу приведу свою реализацию таких деревьев
Заметим, что по сравнению с обычными декартовыми деревьями, у нас в стру��туре появилось свойство width — ширина отрезка, хранимого в нашей текущей вершине. А val — само значение.
Еще одно кардинальное изменение — функция split
Здесь появился еще одна часть if. Которая относится к разделению отрезка на 2 подотрезка. И потом склейка в левую и правую часть.
Функцию merge менять не будем, т. к. в задаче, для которой я это использую на асимптотику это не влияет. Этим я решаю задачу сжатия пространства, и могу отвечать на запросы в онлайн. В принципе, если кому надо, можно переписать и функцию mergeб для объединения двух отрезков. Вместо того, чтобы считывать все возможные данные, задавать фиксированные отрезки, а потом бинпоиском искать нужные номера отрезков, и в дереве отрезков делать нужные изменения, я, за туже асимптотику обхожусь небольшим if. Правда приходится использовать декартовы деревья. Для меня так писать гораздо удобнее и быстрее. Может это кому — нибудь пригодится.
Теперь попробуем сжимать пространство с помощью такого метода и иметь возможность выполнять онлайн операции с любым отрезком.
Сразу приведу свою реализацию таких деревьев
struct treap_node { int x, y; treap_node *l, *r, *p; int width; int val; // Проталкивание ленивых операций void push() { // Место для вставки кода отложенных операций } // Сыновья изменились, нужно обновить текущую вершину void update() { x = width; if (l) x += l -> x; if (r) x += r -> x; if (l) l -> p = this; if (r) r -> p = this; // Вставить код обновления, в зависимости от того, что делает наша структура данных } treap_node(int _width, int _val) { // начальная инициализация // val - хранимое значение, width - ширина хранимого отрезка y = (rand() << 16) + rand(); l = r = p = NULL; width = _width; val = _val; update(); } }; // объединить 2 дерева treap_node* merge(treap_node *l, treap_node *r) { if (l == NULL) return r; if (r == NULL) return l; if (l -> y >= r -> y) { l -> push(); l -> r = merge(l -> r, r); l -> update(); return l; } else { r -> push(); r -> l = merge(l, r -> l); r -> update(); return r; } } // Разрезать 1 дерево на 2 void split(treap_node *t, int x, treap_node *&l, treap_node *&r) { if (t == NULL) { l = r = NULL; return; } t -> push(); if ((t -> l == NULL ? 0 : t -> l -> x) >= x) { split(t -> l, x, l, t -> l); if (l != NULL) l -> p = NULL; t -> update(); r = t; return; } else if ((t -> l == NULL ? 0 : t -> l -> x) + t -> width <= x) { split(t -> r, x - (t -> l == NULL ? 0 : t -> l -> x) - t -> width, t -> r, r); if (r != NULL) r -> p = NULL; t -> update(); l = t; return; } else { // Самая интересная часть. Если граница раздела приходится на сам элемент, то нужно распилить сам элемент treap_node *t1 = new treap_node(x - (t -> l == NULL ? 0 : t -> l -> x), t -> val); treap_node *t2 = new treap_node(t -> width - t1 -> width, t -> val); l = merge(t -> l, t1); r = merge(t2, t -> r); l -> p = NULL; r -> p = NULL; delete(t); } }
Заметим, что по сравнению с обычными декартовыми деревьями, у нас в стру��туре появилось свойство width — ширина отрезка, хранимого в нашей текущей вершине. А val — само значение.
Еще одно кардинальное изменение — функция split
else { // Самая интересная часть. Если граница раздела приходится на сам элемент, то нужно распилить сам элемент treap_node *t1 = new treap_node(x - (t -> l == NULL ? 0 : t -> l -> x), t -> val); treap_node *t2 = new treap_node(t -> width - t1 -> width, t -> val); l = merge(t -> l, t1); r = merge(t2, t -> r); l -> p = NULL; r -> p = NULL; delete(t); }
Здесь появился еще одна часть if. Которая относится к разделению отрезка на 2 подотрезка. И потом склейка в левую и правую часть.
Функцию merge менять не будем, т. к. в задаче, для которой я это использую на асимптотику это не влияет. Этим я решаю задачу сжатия пространства, и могу отвечать на запросы в онлайн. В принципе, если кому надо, можно переписать и функцию mergeб для объединения двух отрезков. Вместо того, чтобы считывать все возможные данные, задавать фиксированные отрезки, а потом бинпоиском искать нужные номера отрезков, и в дереве отрезков делать нужные изменения, я, за туже асимптотику обхожусь небольшим if. Правда приходится использовать декартовы деревья. Для меня так писать гораздо удобнее и быстрее. Может это кому — нибудь пригодится.