Дерево Li Chao: различия между версиями

Материал из Algocode wiki
Перейти к: навигация, поиск
м
м
Строка 1: Строка 1:
 
Дерево Li Chao --- это структура данных, умеющая обрабатывать два вида запросов:
 
Дерево Li Chao --- это структура данных, умеющая обрабатывать два вида запросов:
 
* Добавить линейную функцию $f(x) = ax + b$ в множество $X$.
 
* Добавить линейную функцию $f(x) = ax + b$ в множество $X$.
* Найти минимальное значение $f(x)$ по всем $f \in X$ при заданном $x$.
+
* Найти максимальное значение $f(x)$ по всем $f \in X$ при заданном $x$.
  
 
====Структура дерева====
 
====Структура дерева====
Строка 9: Строка 9:
 
* Сделаем спуск по дереву к листу, соответствующему координате $x$
 
* Сделаем спуск по дереву к листу, соответствующему координате $x$
 
* Выпишем все посещенные вершины
 
* Выпишем все посещенные вершины
* Возьмем минимум среди всех этих функций
+
* Возьмем максимум среди всех этих функций
  
 
Теперь придумаем такие $add$-запросы, что после их выполнения $get$ корректен:
 
Теперь придумаем такие $add$-запросы, что после их выполнения $get$ корректен:
 
* Пусть мы хотим добавить функцию $f$
 
* Пусть мы хотим добавить функцию $f$
* Рассмотрим вершину $v$ такую, что <i>вне отрезка</i> $[l, r]$, соответствующему вершине $v$, $f$ никогда не бывает минимальной.
+
* Рассмотрим вершину $v$ такую, что <i>вне отрезка</i> $[l, r]$, соответствующему вершине $v$, $f$ никогда не бывает максимальной.
 
* Посмотрим на среднюю точку для этого отрезка $m = \frac{l + r}{2}$ и на функцию $g$, записанную в $v$.
 
* Посмотрим на среднюю точку для этого отрезка $m = \frac{l + r}{2}$ и на функцию $g$, записанную в $v$.
* Если $f(m) < g(m)$, то поменяем $f, g$ местами.
+
* Если $f(m) > g(m)$, то поменяем $f, g$ местами.
* Заметим, что теперь прямая $f$ может быть минимальной <b>либо</b> на $[l, m - 1]$, <b>либо</b> на $[m + 1, r]$ (если переменные вещественные, то $m \pm \epsilon$)
+
* Заметим, что теперь функция $f$ может быть максимальной <b>либо</b> на $[l, m - 1]$, <b>либо</b> на $[m + 1, r]$ (если переменные вещественные, то $m \pm \epsilon$)
 
* Рекурсивно решим задачу в соответствующем поддереве
 
* Рекурсивно решим задачу в соответствующем поддереве
  
 
====Реализация====
 
====Реализация====
//TODO
+
Очень часто данное дерево надо реализовывать в [[неявном виде|динамические структуры данных]].
 +
 
 +
<syntaxhighlight lang='c++' line='line'>
 +
 
 +
struct line {
 +
int k = 0, m = 0;
 +
line() {}
 +
line(int k, int m): k(k), m(m) {}
 +
int get(int x) {
 +
return k * x + m;
 +
}
 +
};
 +
 
 +
line t[4 * MAXN];
 +
 
 +
void upd(int v, int tl, int tr, line L) {
 +
if (tl > tr) {
 +
return;
 +
}
 +
int tm = (tl + tr) / 2;
 +
bool l = L.get(tl) > t[v].get(tl);
 +
bool mid = L.get(tm) > t[v].get(tm);
 +
if (mid) {
 +
swap(L, t[v]);
 +
}
 +
if (l != mid) {
 +
upd(2 * v, tl, tm - 1, L);
 +
}
 +
else {
 +
upd(2 * v + 1, tm + 1, tr, L);
 +
}
 +
}
 +
 
 +
int get(int v, int tl, int tr, int x) {
 +
if (tl > tr) {
 +
return 0;
 +
}
 +
int tm = (tl + tr) / 2;
 +
    if (x == tm) {
 +
        return t[v].get(x);
 +
    }
 +
    if (x < tm) {
 +
return max(t[v].get(x), get(2 * v, tl, tm - 1, x));
 +
}
 +
else {
 +
return max(t[v].get(x), get(2 * v + 1, tm + 1, tr, x));
 +
}
 +
}
 +
</syntaxhighlight>
 +
 
  
  
 
{{Автор|Константин Амеличев|kik0s}}
 
{{Автор|Константин Амеличев|kik0s}}
 
[[Категория:Оптимизации динамики]]
 
[[Категория:Оптимизации динамики]]

Версия 08:34, 27 сентября 2019

Дерево Li Chao --- это структура данных, умеющая обрабатывать два вида запросов:

  • Добавить линейную функцию $f(x) = ax + b$ в множество $X$.
  • Найти максимальное значение $f(x)$ по всем $f \in X$ при заданном $x$.

Структура дерева

Построим дерево, аналогичное дерево отрезков, над всем множеством координат (если координаты вещественные, то над всем множеством с определенной точностью). В каждой вершине дерева будем хранить какую-то линейную функцию (изначально нейтральную).

Определим $get$-запросы для координаты $x$ над деревом таким образом:

  • Сделаем спуск по дереву к листу, соответствующему координате $x$
  • Выпишем все посещенные вершины
  • Возьмем максимум среди всех этих функций

Теперь придумаем такие $add$-запросы, что после их выполнения $get$ корректен:

  • Пусть мы хотим добавить функцию $f$
  • Рассмотрим вершину $v$ такую, что вне отрезка $[l, r]$, соответствующему вершине $v$, $f$ никогда не бывает максимальной.
  • Посмотрим на среднюю точку для этого отрезка $m = \frac{l + r}{2}$ и на функцию $g$, записанную в $v$.
  • Если $f(m) > g(m)$, то поменяем $f, g$ местами.
  • Заметим, что теперь функция $f$ может быть максимальной либо на $[l, m - 1]$, либо на $[m + 1, r]$ (если переменные вещественные, то $m \pm \epsilon$)
  • Рекурсивно решим задачу в соответствующем поддереве

Реализация

Очень часто данное дерево надо реализовывать в динамические структуры данных.

 1 struct line {
 2 	int k = 0, m = 0;
 3 	line() {}
 4 	line(int k, int m): k(k), m(m) {}
 5 	int get(int x) {
 6 		return k * x + m;
 7 	}
 8 };
 9 
10 line t[4 * MAXN];
11 
12 void upd(int v, int tl, int tr, line L) {
13 	if (tl > tr) {
14 		return;
15 	}
16 	int tm = (tl + tr) / 2;
17 	bool l = L.get(tl) > t[v].get(tl);
18 	bool mid = L.get(tm) > t[v].get(tm);
19 	if (mid) {
20 		swap(L, t[v]);
21 	}
22 	if (l != mid) {
23 		upd(2 * v, tl, tm - 1, L);
24 	}
25 	else {
26 		upd(2 * v + 1, tm + 1, tr, L);
27 	}
28 }
29 
30 int get(int v, int tl, int tr, int x) {
31 	if (tl > tr) {
32 		return 0;
33 	}
34 	int tm = (tl + tr) / 2;
35     if (x == tm) {
36         return t[v].get(x);
37     }	
38     if (x < tm) {
39 		return max(t[v].get(x), get(2 * v, tl, tm - 1, x));
40 	}
41 	else {
42 		return max(t[v].get(x), get(2 * v + 1, tm + 1, tr, x));
43 	}
44 }




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

По всем вопросам пишите в telegram @kik0s