<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ru">
		<id>http://wiki.atp-fivt.org/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Algocourselecturenotes</id>
		<title>Public ATP Wiki - Вклад участника [ru]</title>
		<link rel="self" type="application/atom+xml" href="http://wiki.atp-fivt.org/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Algocourselecturenotes"/>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php/%D0%A1%D0%BB%D1%83%D0%B6%D0%B5%D0%B1%D0%BD%D0%B0%D1%8F:%D0%92%D0%BA%D0%BB%D0%B0%D0%B4/Algocourselecturenotes"/>
		<updated>2026-04-11T03:54:33Z</updated>
		<subtitle>Вклад участника</subtitle>
		<generator>MediaWiki 1.30.0</generator>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=%D0%90%D0%92%D0%9B-%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE&amp;diff=438</id>
		<title>АВЛ-дерево</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=%D0%90%D0%92%D0%9B-%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE&amp;diff=438"/>
				<updated>2020-05-25T00:44:13Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: Новая страница: «==АВЛ-дерево== ===Определение и свойства=== '''АВЛ-дерево''' - сбалансированное по высоте двоич…»&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==АВЛ-дерево==&lt;br /&gt;
===Определение и свойства===&lt;br /&gt;
'''АВЛ-дерево''' - сбалансированное по высоте двоичное дерево поиска, высоты поддеревьев каждого узла которого различаются не более, чем на 1.&lt;br /&gt;
&lt;br /&gt;
Аббревиатура АВЛ происходит от первых букв фамилий создателей АВЛ-дерева советских учёных Георгия Максимовича Адельсон-Вельского и Евгения Михайловича Ландиса.&lt;br /&gt;
&lt;br /&gt;
==Высота дерева==&lt;br /&gt;
===Лемма о высоте===&lt;br /&gt;
&lt;br /&gt;
Пусть m&amp;lt;sub&amp;gt;h&amp;lt;/sub&amp;gt; - минимальное число вершин в АВЛ-дереве высотой h, тогда m&amp;lt;sub&amp;gt;h&amp;lt;/sub&amp;gt; = F&amp;lt;sub&amp;gt;h+2&amp;lt;/sub&amp;gt; - 1, где F&amp;lt;sub&amp;gt;h&amp;lt;/sub&amp;gt; - h-ое число Фибоначчи.&lt;br /&gt;
&lt;br /&gt;
===Доказательство леммы===&lt;br /&gt;
&lt;br /&gt;
Из свойств АВЛ-дерева следует, что m&amp;lt;sub&amp;gt;h+2&amp;lt;/sub&amp;gt; = m&amp;lt;sub&amp;gt;h&amp;lt;/sub&amp;gt; + m&amp;lt;sub&amp;gt;h+1&amp;lt;/sub&amp;gt; + 1 (два поддерева высотами h и h+1 и их вершина-родитель).&lt;br /&gt;
&lt;br /&gt;
Докажем по индукции равенство m&amp;lt;sub&amp;gt;h&amp;lt;/sub&amp;gt; = F&amp;lt;sub&amp;gt;h+2&amp;lt;/sub&amp;gt; - 1.&lt;br /&gt;
&lt;br /&gt;
База индукции: m&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; = F&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt; - 1 верно, т.к. m&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; = 1, F&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt; = 2.&lt;br /&gt;
&lt;br /&gt;
Тогда имеем m&amp;lt;sub&amp;gt;h+1&amp;lt;/sub&amp;gt; = m&amp;lt;sub&amp;gt;h&amp;lt;/sub&amp;gt; + m&amp;lt;sub&amp;gt;h-1&amp;lt;/sub&amp;gt; + 1 = F&amp;lt;sub&amp;gt;h+2&amp;lt;/sub&amp;gt; - 1 + F&amp;lt;sub&amp;gt;h+1&amp;lt;/sub&amp;gt; - 1 + 1 = F&amp;lt;sub&amp;gt;h+3&amp;lt;/sub&amp;gt; - 1.&lt;br /&gt;
&lt;br /&gt;
Таким образом, равенство m&amp;lt;sub&amp;gt;h&amp;lt;/sub&amp;gt; = F&amp;lt;sub&amp;gt;h+2&amp;lt;/sub&amp;gt; - 1 доказано.&lt;br /&gt;
&lt;br /&gt;
===Высота дерева===&lt;br /&gt;
&lt;br /&gt;
F&amp;lt;sub&amp;gt;h&amp;lt;/sub&amp;gt; = Ω(φ&amp;lt;sup&amp;gt;h&amp;lt;/sup&amp;gt;), φ=(√5+1)/2. &lt;br /&gt;
&lt;br /&gt;
То есть n ⩾ φ&amp;lt;sup&amp;gt;h&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Прологарифмировав обе части, получим:&lt;br /&gt;
&lt;br /&gt;
log&amp;lt;sub&amp;gt;φ&amp;lt;/sub&amp;gt;n ⩾ h&lt;br /&gt;
&lt;br /&gt;
Таким образом, высота АВЛ-дерева - O(log n).&lt;br /&gt;
&lt;br /&gt;
==Балансировка==&lt;br /&gt;
 Для поддержания сбалансированности дерева будем в каждой вершине хранить разность высот её поддеревьев diff[i].&lt;br /&gt;
&lt;br /&gt;
'''Балансировка вершины''' - операция, которая в случае, если разность высот правого и левого поддеревьев АВЛ-дерева равна 2, изменяет связи &amp;quot;предок-потомок&amp;quot; в поддеревьях данной вершины так, что разность вершин в данном дереве составляет по модулю ⩽ 1. Операция осуществляется посредством вращения поддерева данной вершины.&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=%D0%9D%D0%B0%D0%B8%D0%B2%D0%BD%D0%BE%D0%B5_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE_%D0%BF%D0%BE%D0%B8%D1%81%D0%BA%D0%B0&amp;diff=437</id>
		<title>Наивное дерево поиска</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=%D0%9D%D0%B0%D0%B8%D0%B2%D0%BD%D0%BE%D0%B5_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE_%D0%BF%D0%BE%D0%B8%D1%81%D0%BA%D0%B0&amp;diff=437"/>
				<updated>2020-05-24T21:58:40Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: /* Удаление */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Определение и свойства==&lt;br /&gt;
'''Двоичное дерево поиска''' (англ. - ''Binary Search tree - BST'') - структура данных двоичное дерево, для любого узла которого выполняются следующие свойства: &lt;br /&gt;
# Оба узла поддерева являются двоичными деревьями поиска;&lt;br /&gt;
# Все значения ключей левого поддерева меньше ключа узла;&lt;br /&gt;
# Все значения ключей правого поддерева больше ключа узла.&lt;br /&gt;
[[Файл:BST.png|200 px|right|thumb|Двоичное дерево поиска]]&lt;br /&gt;
==Операции над деревом==&lt;br /&gt;
===Поиск===&lt;br /&gt;
====Алгоритм====&lt;br /&gt;
Поиск узла по заданному ключу можно осуществлять рекурсивно, начиная из вершины.&lt;br /&gt;
# Если узел отсутствует, возвращаем нулевой указатель.&lt;br /&gt;
# Если значение в узле равно ключу, возвращаем указатель на узел.&lt;br /&gt;
# Если значение в узле больше ключа, запускаем поиск по ключу в левом поддереве.&lt;br /&gt;
# Если значение в узле меньше ключа, запускаем поиск по ключу в правом поддереве.&lt;br /&gt;
====Реализация на языке C====&lt;br /&gt;
node_t* find (node_t* head, elem_t key) {&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;'''if''' (!head)&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;'''return''' ''NULL'';&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;if (head-&amp;gt;data == key)&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;'''return''' head;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;'''if''' (head-&amp;gt;data &amp;gt; key)&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;'''return''' find(head-&amp;gt;left, key);&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;'''if''' (head-&amp;gt;data &amp;lt; key)&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;'''return''' find(head-&amp;gt;right, key);&amp;lt;br&amp;gt;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
===Вставка===&lt;br /&gt;
====Алгоритм====&lt;br /&gt;
Вставку ключа, как и поиск, будем осуществлять рекурсивно, начиная из вершины.&lt;br /&gt;
# Если узел пустой, вставляем на его место новый узел с ключом.&lt;br /&gt;
# Если ключ равен значению в узле, возвращаемся и ничего не добавляем.&lt;br /&gt;
# Если ключ меньше значения в узле, рекурсивно вызываем вставку в левом поддереве.&lt;br /&gt;
# Если ключ больше значения в узле, рекурсивно вызываем вставку в правом поддереве.&lt;br /&gt;
====Реализация на языке C====&lt;br /&gt;
void insert (node_t* head, T key) {&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp; '''if''' (head-&amp;gt;data == key)&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp; '''return''';&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp; '''if''' (head-&amp;gt;data &amp;gt; key) {&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp; '''if''' (head-&amp;gt;left == ''null'') {&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;&amp;amp;emsp; node_t* newnode = node_create(key);&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;&amp;amp;emsp; newnode-&amp;gt;parent = head;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;&amp;amp;emsp; head-&amp;gt;left = newnode;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;&amp;amp;emsp; '''return''';&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp; }&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp; insert(head-&amp;gt;left, key);&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp; }&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp; '''if''' (head-&amp;gt;data &amp;lt; key) {&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp; '''if''' (head-&amp;gt;right == ''null'') {&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;&amp;amp;emsp; node_t* newnode = node_create(key);&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;&amp;amp;emsp; newnode-&amp;gt;parent = head;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;&amp;amp;emsp; head-&amp;gt;right = newnode;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;&amp;amp;emsp; '''return''';&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp; }&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp; insert(head-&amp;gt;right, key);&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp; }&amp;lt;br&amp;gt;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
===Удаление===&lt;br /&gt;
====Алгоритм====&lt;br /&gt;
[[Файл:BSTDelete.png|500 px|right|thumb|Удаление элемента с двумя детьми]]&lt;br /&gt;
Удаление вершины по ключу будем также осуществлять рекурсивно, начиная из вершины.&lt;br /&gt;
# Если вершина пуста, возвращаемся.&lt;br /&gt;
# Если значение в вершине больше ключа, вызываем удаление для левого поддерева.&lt;br /&gt;
# Если значение в вершине меньше ключа, вызываем удаление для правого поддерева.&lt;br /&gt;
# Если значение в вершине равно ключу, возможны следующие три случая:&lt;br /&gt;
## Правый и левый дети являются пустыми. Тогда просто удаляем узел, обнуляя указатель у родителя.&lt;br /&gt;
## Один из детей является пустым. В таком случае удаляем вершину, присоединяя ребёнка к родителю удалённого узла.&lt;br /&gt;
## Оба ребёнка присутствуют. В таком случае находим элемент u, следующий за удаляемым (для этого, начиная с правого ребёнка удаляемого, совершаем проход влево), и помещаем его значение в удаляемый элемент, после чего удаляем элемент u.&lt;br /&gt;
&lt;br /&gt;
==Асимптотическая сложность операций==&lt;br /&gt;
Асимптотическая сложность операций поиска, вставки и удаления составляет O(h), где h - высота дерева. &lt;br /&gt;
&lt;br /&gt;
Отметим при этом, что в худшем случае возможно вырождение дерева в список (например, при последовательном добавлении элементов отсортированного массива), в связи с чем возникает потребность в балансировке дерева, осуществляемой в различных видах деревьев поиска (Декартово дерево, АВЛ-дерево, Splay-дерево, Красно-чёрное дерево, B-дерево).&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:BSTDelete.png&amp;diff=436</id>
		<title>Файл:BSTDelete.png</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:BSTDelete.png&amp;diff=436"/>
				<updated>2020-05-24T21:57:51Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=%D0%9D%D0%B0%D0%B8%D0%B2%D0%BD%D0%BE%D0%B5_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE_%D0%BF%D0%BE%D0%B8%D1%81%D0%BA%D0%B0&amp;diff=435</id>
		<title>Наивное дерево поиска</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=%D0%9D%D0%B0%D0%B8%D0%B2%D0%BD%D0%BE%D0%B5_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE_%D0%BF%D0%BE%D0%B8%D1%81%D0%BA%D0%B0&amp;diff=435"/>
				<updated>2020-05-24T21:57:20Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: /* Асимптотическая сложность */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Определение и свойства==&lt;br /&gt;
'''Двоичное дерево поиска''' (англ. - ''Binary Search tree - BST'') - структура данных двоичное дерево, для любого узла которого выполняются следующие свойства: &lt;br /&gt;
# Оба узла поддерева являются двоичными деревьями поиска;&lt;br /&gt;
# Все значения ключей левого поддерева меньше ключа узла;&lt;br /&gt;
# Все значения ключей правого поддерева больше ключа узла.&lt;br /&gt;
[[Файл:BST.png|200 px|right|thumb|Двоичное дерево поиска]]&lt;br /&gt;
==Операции над деревом==&lt;br /&gt;
===Поиск===&lt;br /&gt;
====Алгоритм====&lt;br /&gt;
Поиск узла по заданному ключу можно осуществлять рекурсивно, начиная из вершины.&lt;br /&gt;
# Если узел отсутствует, возвращаем нулевой указатель.&lt;br /&gt;
# Если значение в узле равно ключу, возвращаем указатель на узел.&lt;br /&gt;
# Если значение в узле больше ключа, запускаем поиск по ключу в левом поддереве.&lt;br /&gt;
# Если значение в узле меньше ключа, запускаем поиск по ключу в правом поддереве.&lt;br /&gt;
====Реализация на языке C====&lt;br /&gt;
node_t* find (node_t* head, elem_t key) {&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;'''if''' (!head)&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;'''return''' ''NULL'';&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;if (head-&amp;gt;data == key)&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;'''return''' head;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;'''if''' (head-&amp;gt;data &amp;gt; key)&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;'''return''' find(head-&amp;gt;left, key);&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;'''if''' (head-&amp;gt;data &amp;lt; key)&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;'''return''' find(head-&amp;gt;right, key);&amp;lt;br&amp;gt;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
===Вставка===&lt;br /&gt;
====Алгоритм====&lt;br /&gt;
Вставку ключа, как и поиск, будем осуществлять рекурсивно, начиная из вершины.&lt;br /&gt;
# Если узел пустой, вставляем на его место новый узел с ключом.&lt;br /&gt;
# Если ключ равен значению в узле, возвращаемся и ничего не добавляем.&lt;br /&gt;
# Если ключ меньше значения в узле, рекурсивно вызываем вставку в левом поддереве.&lt;br /&gt;
# Если ключ больше значения в узле, рекурсивно вызываем вставку в правом поддереве.&lt;br /&gt;
====Реализация на языке C====&lt;br /&gt;
void insert (node_t* head, T key) {&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp; '''if''' (head-&amp;gt;data == key)&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp; '''return''';&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp; '''if''' (head-&amp;gt;data &amp;gt; key) {&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp; '''if''' (head-&amp;gt;left == ''null'') {&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;&amp;amp;emsp; node_t* newnode = node_create(key);&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;&amp;amp;emsp; newnode-&amp;gt;parent = head;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;&amp;amp;emsp; head-&amp;gt;left = newnode;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;&amp;amp;emsp; '''return''';&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp; }&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp; insert(head-&amp;gt;left, key);&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp; }&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp; '''if''' (head-&amp;gt;data &amp;lt; key) {&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp; '''if''' (head-&amp;gt;right == ''null'') {&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;&amp;amp;emsp; node_t* newnode = node_create(key);&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;&amp;amp;emsp; newnode-&amp;gt;parent = head;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;&amp;amp;emsp; head-&amp;gt;right = newnode;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;&amp;amp;emsp; '''return''';&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp; }&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp; insert(head-&amp;gt;right, key);&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp; }&amp;lt;br&amp;gt;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
===Удаление===&lt;br /&gt;
====Алгоритм====&lt;br /&gt;
Удаление вершины по ключу будем также осуществлять рекурсивно, начиная из вершины.&lt;br /&gt;
# Если вершина пуста, возвращаемся.&lt;br /&gt;
# Если значение в вершине больше ключа, вызываем удаление для левого поддерева.&lt;br /&gt;
# Если значение в вершине меньше ключа, вызываем удаление для правого поддерева.&lt;br /&gt;
# Если значение в вершине равно ключу, возможны следующие три случая:&lt;br /&gt;
## Правый и левый дети являются пустыми. Тогда просто удаляем узел, обнуляя указатель у родителя.&lt;br /&gt;
## Один из детей является пустым. В таком случае удаляем вершину, присоединяя ребёнка к родителю удалённого узла.&lt;br /&gt;
## Оба ребёнка присутствуют. В таком случае находим элемент u, следующий за удаляемым (для этого, начиная с правого ребёнка удаляемого, совершаем проход влево), и помещаем его значение в удаляемый элемент, после чего удаляем элемент u.&lt;br /&gt;
[[Файл:BST.png|200 px|right|thumb|Удаление элемента с двумя детьми]]&lt;br /&gt;
&lt;br /&gt;
==Асимптотическая сложность операций==&lt;br /&gt;
Асимптотическая сложность операций поиска, вставки и удаления составляет O(h), где h - высота дерева. &lt;br /&gt;
&lt;br /&gt;
Отметим при этом, что в худшем случае возможно вырождение дерева в список (например, при последовательном добавлении элементов отсортированного массива), в связи с чем возникает потребность в балансировке дерева, осуществляемой в различных видах деревьев поиска (Декартово дерево, АВЛ-дерево, Splay-дерево, Красно-чёрное дерево, B-дерево).&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=%D0%9D%D0%B0%D0%B8%D0%B2%D0%BD%D0%BE%D0%B5_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE_%D0%BF%D0%BE%D0%B8%D1%81%D0%BA%D0%B0&amp;diff=434</id>
		<title>Наивное дерево поиска</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=%D0%9D%D0%B0%D0%B8%D0%B2%D0%BD%D0%BE%D0%B5_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE_%D0%BF%D0%BE%D0%B8%D1%81%D0%BA%D0%B0&amp;diff=434"/>
				<updated>2020-05-24T21:56:50Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: /* Операции над деревом */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Определение и свойства==&lt;br /&gt;
'''Двоичное дерево поиска''' (англ. - ''Binary Search tree - BST'') - структура данных двоичное дерево, для любого узла которого выполняются следующие свойства: &lt;br /&gt;
# Оба узла поддерева являются двоичными деревьями поиска;&lt;br /&gt;
# Все значения ключей левого поддерева меньше ключа узла;&lt;br /&gt;
# Все значения ключей правого поддерева больше ключа узла.&lt;br /&gt;
[[Файл:BST.png|200 px|right|thumb|Двоичное дерево поиска]]&lt;br /&gt;
==Операции над деревом==&lt;br /&gt;
===Поиск===&lt;br /&gt;
====Алгоритм====&lt;br /&gt;
Поиск узла по заданному ключу можно осуществлять рекурсивно, начиная из вершины.&lt;br /&gt;
# Если узел отсутствует, возвращаем нулевой указатель.&lt;br /&gt;
# Если значение в узле равно ключу, возвращаем указатель на узел.&lt;br /&gt;
# Если значение в узле больше ключа, запускаем поиск по ключу в левом поддереве.&lt;br /&gt;
# Если значение в узле меньше ключа, запускаем поиск по ключу в правом поддереве.&lt;br /&gt;
====Реализация на языке C====&lt;br /&gt;
node_t* find (node_t* head, elem_t key) {&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;'''if''' (!head)&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;'''return''' ''NULL'';&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;if (head-&amp;gt;data == key)&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;'''return''' head;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;'''if''' (head-&amp;gt;data &amp;gt; key)&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;'''return''' find(head-&amp;gt;left, key);&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;'''if''' (head-&amp;gt;data &amp;lt; key)&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;'''return''' find(head-&amp;gt;right, key);&amp;lt;br&amp;gt;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
===Вставка===&lt;br /&gt;
====Алгоритм====&lt;br /&gt;
Вставку ключа, как и поиск, будем осуществлять рекурсивно, начиная из вершины.&lt;br /&gt;
# Если узел пустой, вставляем на его место новый узел с ключом.&lt;br /&gt;
# Если ключ равен значению в узле, возвращаемся и ничего не добавляем.&lt;br /&gt;
# Если ключ меньше значения в узле, рекурсивно вызываем вставку в левом поддереве.&lt;br /&gt;
# Если ключ больше значения в узле, рекурсивно вызываем вставку в правом поддереве.&lt;br /&gt;
====Реализация на языке C====&lt;br /&gt;
void insert (node_t* head, T key) {&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp; '''if''' (head-&amp;gt;data == key)&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp; '''return''';&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp; '''if''' (head-&amp;gt;data &amp;gt; key) {&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp; '''if''' (head-&amp;gt;left == ''null'') {&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;&amp;amp;emsp; node_t* newnode = node_create(key);&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;&amp;amp;emsp; newnode-&amp;gt;parent = head;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;&amp;amp;emsp; head-&amp;gt;left = newnode;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;&amp;amp;emsp; '''return''';&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp; }&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp; insert(head-&amp;gt;left, key);&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp; }&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp; '''if''' (head-&amp;gt;data &amp;lt; key) {&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp; '''if''' (head-&amp;gt;right == ''null'') {&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;&amp;amp;emsp; node_t* newnode = node_create(key);&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;&amp;amp;emsp; newnode-&amp;gt;parent = head;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;&amp;amp;emsp; head-&amp;gt;right = newnode;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;&amp;amp;emsp; '''return''';&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp; }&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp; insert(head-&amp;gt;right, key);&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp; }&amp;lt;br&amp;gt;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
===Удаление===&lt;br /&gt;
====Алгоритм====&lt;br /&gt;
Удаление вершины по ключу будем также осуществлять рекурсивно, начиная из вершины.&lt;br /&gt;
# Если вершина пуста, возвращаемся.&lt;br /&gt;
# Если значение в вершине больше ключа, вызываем удаление для левого поддерева.&lt;br /&gt;
# Если значение в вершине меньше ключа, вызываем удаление для правого поддерева.&lt;br /&gt;
# Если значение в вершине равно ключу, возможны следующие три случая:&lt;br /&gt;
## Правый и левый дети являются пустыми. Тогда просто удаляем узел, обнуляя указатель у родителя.&lt;br /&gt;
## Один из детей является пустым. В таком случае удаляем вершину, присоединяя ребёнка к родителю удалённого узла.&lt;br /&gt;
## Оба ребёнка присутствуют. В таком случае находим элемент u, следующий за удаляемым (для этого, начиная с правого ребёнка удаляемого, совершаем проход влево), и помещаем его значение в удаляемый элемент, после чего удаляем элемент u.&lt;br /&gt;
[[Файл:BST.png|200 px|right|thumb|Удаление элемента с двумя детьми]]&lt;br /&gt;
&lt;br /&gt;
==Асимптотическая сложность==&lt;br /&gt;
Асимптотическая сложность операций поиска, вставки и удаления составляет O(h), где h - высота дерева. &lt;br /&gt;
&lt;br /&gt;
Отметим при этом, что в худшем случае возможно вырождение дерева в список (например, при последовательном добавлении элементов отсортированного массива), в связи с чем возникает потребность в балансировке дерева, осуществляемой в различных видах деревьев поиска (Декартово дерево, АВЛ-дерево, Splay-дерево, Красно-чёрное дерево, B-дерево).&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=%D0%9D%D0%B0%D0%B8%D0%B2%D0%BD%D0%BE%D0%B5_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE_%D0%BF%D0%BE%D0%B8%D1%81%D0%BA%D0%B0&amp;diff=433</id>
		<title>Наивное дерево поиска</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=%D0%9D%D0%B0%D0%B8%D0%B2%D0%BD%D0%BE%D0%B5_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE_%D0%BF%D0%BE%D0%B8%D1%81%D0%BA%D0%B0&amp;diff=433"/>
				<updated>2020-05-24T21:56:05Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Определение и свойства==&lt;br /&gt;
'''Двоичное дерево поиска''' (англ. - ''Binary Search tree - BST'') - структура данных двоичное дерево, для любого узла которого выполняются следующие свойства: &lt;br /&gt;
# Оба узла поддерева являются двоичными деревьями поиска;&lt;br /&gt;
# Все значения ключей левого поддерева меньше ключа узла;&lt;br /&gt;
# Все значения ключей правого поддерева больше ключа узла.&lt;br /&gt;
[[Файл:BST.png|200 px|right|thumb|Двоичное дерево поиска]]&lt;br /&gt;
==Операции над деревом==&lt;br /&gt;
===Поиск===&lt;br /&gt;
====Алгоритм====&lt;br /&gt;
Поиск узла по заданному ключу можно осуществлять рекурсивно, начиная из вершины.&lt;br /&gt;
# Если узел отсутствует, возвращаем нулевой указатель.&lt;br /&gt;
# Если значение в узле равно ключу, возвращаем указатель на узел.&lt;br /&gt;
# Если значение в узле больше ключа, запускаем поиск по ключу в левом поддереве.&lt;br /&gt;
# Если значение в узле меньше ключа, запускаем поиск по ключу в правом поддереве.&lt;br /&gt;
====Реализация на языке C====&lt;br /&gt;
node_t* find (node_t* head, elem_t key) {&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;'''if''' (!head)&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;'''return''' ''NULL'';&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;if (head-&amp;gt;data == key)&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;'''return''' head;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;'''if''' (head-&amp;gt;data &amp;gt; key)&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;'''return''' find(head-&amp;gt;left, key);&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;'''if''' (head-&amp;gt;data &amp;lt; key)&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;'''return''' find(head-&amp;gt;right, key);&amp;lt;br&amp;gt;&lt;br /&gt;
}&lt;br /&gt;
===Вставка===&lt;br /&gt;
====Алгоритм====&lt;br /&gt;
Вставку ключа, как и поиск, будем осуществлять рекурсивно, начиная из вершины.&lt;br /&gt;
# Если узел пустой, вставляем на его место новый узел с ключом.&lt;br /&gt;
# Если ключ равен значению в узле, возвращаемся и ничего не добавляем.&lt;br /&gt;
# Если ключ меньше значения в узле, рекурсивно вызываем вставку в левом поддереве.&lt;br /&gt;
# Если ключ больше значения в узле, рекурсивно вызываем вставку в правом поддереве.&lt;br /&gt;
====Реализация на языке C====&lt;br /&gt;
void insert (node_t* head, T key) {&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp; '''if''' (head-&amp;gt;data == key)&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp; '''return''';&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp; '''if''' (head-&amp;gt;data &amp;gt; key) {&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp; '''if''' (head-&amp;gt;left == ''null'') {&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;&amp;amp;emsp; node_t* newnode = node_create(key);&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;&amp;amp;emsp; newnode-&amp;gt;parent = head;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;&amp;amp;emsp; head-&amp;gt;left = newnode;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;&amp;amp;emsp; '''return''';&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp; }&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp; insert(head-&amp;gt;left, key);&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp; }&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp; '''if''' (head-&amp;gt;data &amp;lt; key) {&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp; '''if''' (head-&amp;gt;right == ''null'') {&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;&amp;amp;emsp; node_t* newnode = node_create(key);&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;&amp;amp;emsp; newnode-&amp;gt;parent = head;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;&amp;amp;emsp; head-&amp;gt;right = newnode;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;&amp;amp;emsp; '''return''';&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp; }&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp; insert(head-&amp;gt;right, key);&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp; }&amp;lt;br&amp;gt;&lt;br /&gt;
}&lt;br /&gt;
===Удаление===&lt;br /&gt;
====Алгоритм====&lt;br /&gt;
Удаление вершины по ключу будем также осуществлять рекурсивно, начиная из вершины.&lt;br /&gt;
# Если вершина пуста, возвращаемся.&lt;br /&gt;
# Если значение в вершине больше ключа, вызываем удаление для левого поддерева.&lt;br /&gt;
# Если значение в вершине меньше ключа, вызываем удаление для правого поддерева.&lt;br /&gt;
# Если значение в вершине равно ключу, возможны следующие три случая:&lt;br /&gt;
## Правый и левый дети являются пустыми. Тогда просто удаляем узел, обнуляя указатель у родителя.&lt;br /&gt;
## Один из детей является пустым. В таком случае удаляем вершину, присоединяя ребёнка к родителю удалённого узла.&lt;br /&gt;
## Оба ребёнка присутствуют. В таком случае находим элемент u, следующий за удаляемым (для этого, начиная с правого ребёнка удаляемого, совершаем проход влево), и помещаем его значение в удаляемый элемент, после чего удаляем элемент u.&lt;br /&gt;
[[Файл:BST.png|200 px|right|thumb|Удаление элемента с двумя детьми]]&lt;br /&gt;
==Асимптотическая сложность==&lt;br /&gt;
Асимптотическая сложность операций поиска, вставки и удаления составляет O(h), где h - высота дерева. &lt;br /&gt;
&lt;br /&gt;
Отметим при этом, что в худшем случае возможно вырождение дерева в список (например, при последовательном добавлении элементов отсортированного массива), в связи с чем возникает потребность в балансировке дерева, осуществляемой в различных видах деревьев поиска (Декартово дерево, АВЛ-дерево, Splay-дерево, Красно-чёрное дерево, B-дерево).&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=%D0%9D%D0%B0%D0%B8%D0%B2%D0%BD%D0%BE%D0%B5_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE_%D0%BF%D0%BE%D0%B8%D1%81%D0%BA%D0%B0&amp;diff=432</id>
		<title>Наивное дерево поиска</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=%D0%9D%D0%B0%D0%B8%D0%B2%D0%BD%D0%BE%D0%B5_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE_%D0%BF%D0%BE%D0%B8%D1%81%D0%BA%D0%B0&amp;diff=432"/>
				<updated>2020-05-24T19:39:02Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: Новая страница: «==Определение и свойства== '''Двоичное дерево поиска''' (англ. - ''Binary Search tree - BST'') - структура д…»&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Определение и свойства==&lt;br /&gt;
'''Двоичное дерево поиска''' (англ. - ''Binary Search tree - BST'') - структура данных двоичное дерево, для любого узла которого выполняются следующие свойства: &lt;br /&gt;
# Оба узла поддерева являются двоичными деревьями поиска;&lt;br /&gt;
# Все значения ключей левого поддерева меньше ключа узла;&lt;br /&gt;
# Все значения ключей правого поддерева больше ключа узла.&lt;br /&gt;
[[Файл:BST.png|200 px|right|thumb|Двоичное дерево поиска]]&lt;br /&gt;
==Операции над деревом==&lt;br /&gt;
===Поиск===&lt;br /&gt;
====Алгоритм====&lt;br /&gt;
Поиск узла по заданному ключу можно осуществлять рекурсивно, начиная из вершины.&lt;br /&gt;
# Если узел отсутствует, возвращаем нулевой указатель.&lt;br /&gt;
# Если значение в узле равно ключу, возвращаем указатель на узел.&lt;br /&gt;
# Если значение в узле больше ключа, запускаем поиск по ключу в левом поддереве.&lt;br /&gt;
# Если значение в узле меньше ключа, запускаем поиск по ключу в правом поддереве.&lt;br /&gt;
====Реализация на языке C====&lt;br /&gt;
node_t* find (node_t* head, elem_t key) {&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;'''if''' (!head)&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;'''return''' ''NULL'';&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;if (head-&amp;gt;data == key)&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;'''return''' head;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;'''if''' (head-&amp;gt;data &amp;gt; key)&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;'''return''' find(head-&amp;gt;left, key);&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;'''if''' (head-&amp;gt;data &amp;lt; key)&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;'''return''' find(head-&amp;gt;right, key);&amp;lt;br&amp;gt;&lt;br /&gt;
}&lt;br /&gt;
===Вставка===&lt;br /&gt;
====Алгоритм====&lt;br /&gt;
Вставку ключа, как и поиск, будем осуществлять рекурсивно, начиная из вершины.&lt;br /&gt;
# Если узел пустой, вставляем на его место новый узел с ключом.&lt;br /&gt;
# Если ключ равен значению в узле, возвращаемся и ничего не добавляем.&lt;br /&gt;
# Если ключ меньше значения в узле, рекурсивно вызываем вставку в левом поддереве.&lt;br /&gt;
# Если ключ больше значения в узле, рекурсивно вызываем вставку в правом поддереве.&lt;br /&gt;
====Реализация на языке C====&lt;br /&gt;
void insert (node_t* head, T key) {&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp; '''if''' (head-&amp;gt;data == key)&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp; '''return''';&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp; '''if''' (head-&amp;gt;data &amp;gt; key) {&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp; '''if''' (head-&amp;gt;left == ''null'') {&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;&amp;amp;emsp; node_t* newnode = node_create(key);&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;&amp;amp;emsp; newnode-&amp;gt;parent = head;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;&amp;amp;emsp; head-&amp;gt;left = newnode;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;&amp;amp;emsp; '''return''';&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp; }&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp; insert(head-&amp;gt;left, key);&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp; }&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp; '''if''' (head-&amp;gt;data &amp;lt; key) {&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp; '''if''' (head-&amp;gt;right == ''null'') {&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;&amp;amp;emsp; node_t* newnode = node_create(key);&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;&amp;amp;emsp; newnode-&amp;gt;parent = head;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;&amp;amp;emsp; head-&amp;gt;right = newnode;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp;&amp;amp;emsp; '''return''';&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp; }&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp;&amp;amp;emsp; insert(head-&amp;gt;right, key);&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;amp;emsp; }&amp;lt;br&amp;gt;&lt;br /&gt;
}&lt;br /&gt;
===Удаление===&lt;br /&gt;
====Алгоритм====&lt;br /&gt;
Удаление вершины по ключу будем также осуществлять рекурсивно, начиная из вершины.&lt;br /&gt;
# Если вершина пуста, возвращаемся.&lt;br /&gt;
# Если вершина пуста&lt;br /&gt;
#&lt;br /&gt;
#&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:BST.png&amp;diff=431</id>
		<title>Файл:BST.png</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:BST.png&amp;diff=431"/>
				<updated>2020-05-24T18:38:52Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=%D0%94%D0%B5%D1%80%D0%B5%D0%B2%D0%BE_%D0%BE%D1%82%D1%80%D0%B5%D0%B7%D0%BA%D0%BE%D0%B2&amp;diff=425</id>
		<title>Дерево отрезков</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=%D0%94%D0%B5%D1%80%D0%B5%D0%B2%D0%BE_%D0%BE%D1%82%D1%80%D0%B5%D0%B7%D0%BA%D0%BE%D0%B2&amp;diff=425"/>
				<updated>2020-05-15T09:57:06Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: /* Пример задачи */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Общая идея ==&lt;br /&gt;
Очень похоже на sparse table - хотим предподсчитывать какие-то значения для отрезков, и обновлять их при изменении дерева.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
''Замечание: рассматриваем на примере задачи RMQ из конспекта про sparse table''&lt;br /&gt;
&lt;br /&gt;
== Структура ==&lt;br /&gt;
На нижнем уровне располагаются элементы массива, на уровень выше - узлы, в которых минимум из двух элементов, на уровень выше - минимум из соответствующих соседей предыдущего уровня и так далее. Рисунок для случая, когда количество элементов в массиве - степень двойки. &lt;br /&gt;
[[Файл:vis_tree.png]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Разумеется, уровней всего O(log n). На i-м уровне, начиная от самого нижнего, n / 2^i элементов. Таким образом, все дерево можно построить (просто сумма членов геометрической прогрессии) за O(n). &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Обработка запроса ==&lt;br /&gt;
Общая идея: процедура getMin начинает от корня дерева и рекурсивно спускается в те отрезки, которые ее интересуют. Пусть мы находимся в вершине x, которая соответствует полуинтервалу TreeSeg = [L_tree, R_tree), а нам нужен минимум на полуинтервале QuerySeg = [L_q, R_q). Строго говоря, возможны три случая:&amp;lt;br&amp;gt;&lt;br /&gt;
1. TreeSeg ⊆ QuerySeg. Тогда необходимо вернуть значение из вершины. &amp;lt;br&amp;gt;&lt;br /&gt;
2. TreeSeg ⋂ QuerySeg = ∅. Необходимо вернуть какую-то большую константу IntMax, или какой-то флаг, показывающий, что отрезки не пересекаются - чтобы уже &amp;quot;вышестоящая вершина&amp;quot; думала над тем, что с этим знанием делать. &amp;lt;br&amp;gt;&lt;br /&gt;
3. TreeSeg ⋂ QuerySeg ≠ ∅. Тогда, разумеется, для обработки запроса необходимо вернуть min (getMin (left_half (x)), getMin (right_half (x))). Заметим, что в силу того что left_half и right_half не пересекаются, мы в будущем сможем решать более сложные задачи с помощью этой же структуры.&lt;br /&gt;
&lt;br /&gt;
{{Template:Lemma&lt;br /&gt;
|What = Лемма 1&lt;br /&gt;
|Statement = Каждый такой запрос выполняется за O(log n). &lt;br /&gt;
|Proof = Понятно, что оценка асимптотики алгоритма сводится к подсчету количества рекурсий, которые наша процедура потребует для выполнения. Рассмотрим произвольный уровень нашего дерева при запросе. На нем мы сделаем не более двух рекурсий вниз по дереву (случай 3). Таким образом, количество рекурсий ограничено O(1) * O(height) = O(log n). &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Изменение дерева ==&lt;br /&gt;
''Замечание: на данном этапе предлагается перейти к произвольной функции f элементов на отрезке. Разумеется, нам подойдут не все функции. Необходимо, чтобы эта функция была ассоциативной, поскольку иначе мы не сможет &amp;quot;расставлять скобки&amp;quot; при сведении этой задачи к меньшим. ''&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
''Оговорка: мы храним наше дерево в виде массива - по аналогии с бинарной кучей. ''&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Итак, обрабатываем запрос вида change (i, new_value). Существуют два подхода:&amp;lt;br&amp;gt;&lt;br /&gt;
1. &amp;quot;Изменение снизу&amp;quot;. &amp;lt;br&amp;gt;&lt;br /&gt;
Зная номер вершины i мы можем пересчитать все значения дерева, которые идут на пути от корня до этой вершины (напомним, что индекс родителя при нашей оговорке мы найдем как i / 2). Эта схема плоха тем, что работает только для случая, когда количество элементов - степень двойки. &amp;lt;br&amp;gt;&lt;br /&gt;
2. &amp;quot;Изменение сверху&amp;quot;. &amp;lt;br&amp;gt;&lt;br /&gt;
Аналогично процедуре обработки запроса, мы рекурсивно спускаемся в нужное нам поддерево, переподсчитываем там нашу функцию, после этого обновляем значение в текущей вершины и возвращаем его. &lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Изменение элементов на подотрезке ==&lt;br /&gt;
Пусть нам поступает запрос на присвоение всем элементам с индексами от L до R значения new_value. Вновь применим идею отложенной операции. Будем рекурсивно спускаться от корня пока весь соответствующий текущей вершине полуинтервал не будет лежать в [L, R); когда это произошло, сохраним в этой вершине запрос на изменение. &amp;lt;br&amp;gt;&lt;br /&gt;
Тогда нужно понять, как это повлияет на процедуру  обработки запроса. Разумеется, изменится случай 1 - сейчас нам придется учитывать не только значение вершины, но и эту отложенную операцию, что, впрочем, несильно усложняет дело. Также необходимо в случае 3 &amp;quot;протолкнуть&amp;quot; отложенную операцию в сыновей. Понятно, что никакую асимптотику это нарушить не может. Но здесь важно не забыть после рассмотрения случаев 1, 2, 3 обновить значение в текущей вершине на основе вновь полученной информации о листьях данной вершины. И только после этого можно безопасно возвращаться к родительскому узлу.&lt;br /&gt;
&lt;br /&gt;
== Персистентное дерево отрезков ==&lt;br /&gt;
''Замечание: здесь нам придется отказаться от идеи хранить структуру в виде массива, по аналогии с бинарной кучей.''&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Вдобавок к уже известным функциям и требованием иногда хочется уметь возвращаться к старой версии дерева. Таким образом, мы хотим добавить функцию checkout (version_number), где version_number - количество вызовов функции изменения дерева на подотрезке.&amp;lt;br&amp;gt;&lt;br /&gt;
Заметим, что при каждом вызове функции изменения дерева на подотрезке изменяется лишь O(log n) вершин.&lt;br /&gt;
&amp;lt;br&amp;gt; &lt;br /&gt;
[[Файл:ветвь.png]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Более того, при каждой такой операции изменяется лишь один путь от  корня до листа. Итак, мы может создавать новую версию этого пути, возвращая из функции change указатель на новую версию того самого пути. Тогда в каждой вершине на этом пути один из детей будет обновленным, а другой старым.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Имея на руках систему контроля версий, мы можем считать нашу функцию f от полуинтервала исходного массива не только в актуальной версии дерева, но и в любой его версии, имея массив, в котором индекс - номер версии, а значение - соответствующая подветвь.&lt;br /&gt;
&lt;br /&gt;
== Пример задачи ==&lt;br /&gt;
Имеется статический массив. Запрос - количество элементов с индексами из [L, R), которые лежат во множестве {x, x+1, ... y-1, y}. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
'''Способ 1.''' &amp;lt;br&amp;gt;&lt;br /&gt;
Будем в каждой вершине хранить весь соответсвующий массив в отсортированном виде. Кажется, что о памяти лучше не думать, но на самом деле все относительно прилично: каждый элемент находится от корня на расстоянии O(log n) вершин, поэтому всего дополнительно затрачено будет O(n log n). После этого необходимо найти наибольший элемент, меньший x и наименьший, больший y в массиве нужной вершины. Тогда нужно просто вернуть количество элементов между этими только что найденными. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
'''Способ 2.''' &amp;lt;br&amp;gt;&lt;br /&gt;
Отсортируем элементы по возрастанию (разумеется, с сохранением первоначальной позиции элементов), получив массив S. Создадим пустой массив того же размера E, сделаем на нем дерево отрезков. После этого будем пробегать по массиву S и ставить в массив E единицы в соответствии и сохраненным ранее порядком. После этого достаточно сделать count (L, R, &amp;quot;≤ y&amp;quot;) - count (L, R, &amp;quot;&amp;lt; x&amp;quot;), используя описанную выше систему контроля версий. Памяти мы тратим по-прежнему O(n log n), поскольку у нас n разных ветвей по log(n) каждая.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''Замечание: можно сделать дерево отрезков из деревьев отрезков, чтобы эффективно отвечать на похожие запросы в случае плоскости. ''&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=%D0%94%D0%B5%D1%80%D0%B5%D0%B2%D0%BE_%D0%BE%D1%82%D1%80%D0%B5%D0%B7%D0%BA%D0%BE%D0%B2&amp;diff=424</id>
		<title>Дерево отрезков</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=%D0%94%D0%B5%D1%80%D0%B5%D0%B2%D0%BE_%D0%BE%D1%82%D1%80%D0%B5%D0%B7%D0%BA%D0%BE%D0%B2&amp;diff=424"/>
				<updated>2020-05-15T09:56:55Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: /* Пример задачи */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Общая идея ==&lt;br /&gt;
Очень похоже на sparse table - хотим предподсчитывать какие-то значения для отрезков, и обновлять их при изменении дерева.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
''Замечание: рассматриваем на примере задачи RMQ из конспекта про sparse table''&lt;br /&gt;
&lt;br /&gt;
== Структура ==&lt;br /&gt;
На нижнем уровне располагаются элементы массива, на уровень выше - узлы, в которых минимум из двух элементов, на уровень выше - минимум из соответствующих соседей предыдущего уровня и так далее. Рисунок для случая, когда количество элементов в массиве - степень двойки. &lt;br /&gt;
[[Файл:vis_tree.png]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Разумеется, уровней всего O(log n). На i-м уровне, начиная от самого нижнего, n / 2^i элементов. Таким образом, все дерево можно построить (просто сумма членов геометрической прогрессии) за O(n). &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Обработка запроса ==&lt;br /&gt;
Общая идея: процедура getMin начинает от корня дерева и рекурсивно спускается в те отрезки, которые ее интересуют. Пусть мы находимся в вершине x, которая соответствует полуинтервалу TreeSeg = [L_tree, R_tree), а нам нужен минимум на полуинтервале QuerySeg = [L_q, R_q). Строго говоря, возможны три случая:&amp;lt;br&amp;gt;&lt;br /&gt;
1. TreeSeg ⊆ QuerySeg. Тогда необходимо вернуть значение из вершины. &amp;lt;br&amp;gt;&lt;br /&gt;
2. TreeSeg ⋂ QuerySeg = ∅. Необходимо вернуть какую-то большую константу IntMax, или какой-то флаг, показывающий, что отрезки не пересекаются - чтобы уже &amp;quot;вышестоящая вершина&amp;quot; думала над тем, что с этим знанием делать. &amp;lt;br&amp;gt;&lt;br /&gt;
3. TreeSeg ⋂ QuerySeg ≠ ∅. Тогда, разумеется, для обработки запроса необходимо вернуть min (getMin (left_half (x)), getMin (right_half (x))). Заметим, что в силу того что left_half и right_half не пересекаются, мы в будущем сможем решать более сложные задачи с помощью этой же структуры.&lt;br /&gt;
&lt;br /&gt;
{{Template:Lemma&lt;br /&gt;
|What = Лемма 1&lt;br /&gt;
|Statement = Каждый такой запрос выполняется за O(log n). &lt;br /&gt;
|Proof = Понятно, что оценка асимптотики алгоритма сводится к подсчету количества рекурсий, которые наша процедура потребует для выполнения. Рассмотрим произвольный уровень нашего дерева при запросе. На нем мы сделаем не более двух рекурсий вниз по дереву (случай 3). Таким образом, количество рекурсий ограничено O(1) * O(height) = O(log n). &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Изменение дерева ==&lt;br /&gt;
''Замечание: на данном этапе предлагается перейти к произвольной функции f элементов на отрезке. Разумеется, нам подойдут не все функции. Необходимо, чтобы эта функция была ассоциативной, поскольку иначе мы не сможет &amp;quot;расставлять скобки&amp;quot; при сведении этой задачи к меньшим. ''&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
''Оговорка: мы храним наше дерево в виде массива - по аналогии с бинарной кучей. ''&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Итак, обрабатываем запрос вида change (i, new_value). Существуют два подхода:&amp;lt;br&amp;gt;&lt;br /&gt;
1. &amp;quot;Изменение снизу&amp;quot;. &amp;lt;br&amp;gt;&lt;br /&gt;
Зная номер вершины i мы можем пересчитать все значения дерева, которые идут на пути от корня до этой вершины (напомним, что индекс родителя при нашей оговорке мы найдем как i / 2). Эта схема плоха тем, что работает только для случая, когда количество элементов - степень двойки. &amp;lt;br&amp;gt;&lt;br /&gt;
2. &amp;quot;Изменение сверху&amp;quot;. &amp;lt;br&amp;gt;&lt;br /&gt;
Аналогично процедуре обработки запроса, мы рекурсивно спускаемся в нужное нам поддерево, переподсчитываем там нашу функцию, после этого обновляем значение в текущей вершины и возвращаем его. &lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Изменение элементов на подотрезке ==&lt;br /&gt;
Пусть нам поступает запрос на присвоение всем элементам с индексами от L до R значения new_value. Вновь применим идею отложенной операции. Будем рекурсивно спускаться от корня пока весь соответствующий текущей вершине полуинтервал не будет лежать в [L, R); когда это произошло, сохраним в этой вершине запрос на изменение. &amp;lt;br&amp;gt;&lt;br /&gt;
Тогда нужно понять, как это повлияет на процедуру  обработки запроса. Разумеется, изменится случай 1 - сейчас нам придется учитывать не только значение вершины, но и эту отложенную операцию, что, впрочем, несильно усложняет дело. Также необходимо в случае 3 &amp;quot;протолкнуть&amp;quot; отложенную операцию в сыновей. Понятно, что никакую асимптотику это нарушить не может. Но здесь важно не забыть после рассмотрения случаев 1, 2, 3 обновить значение в текущей вершине на основе вновь полученной информации о листьях данной вершины. И только после этого можно безопасно возвращаться к родительскому узлу.&lt;br /&gt;
&lt;br /&gt;
== Персистентное дерево отрезков ==&lt;br /&gt;
''Замечание: здесь нам придется отказаться от идеи хранить структуру в виде массива, по аналогии с бинарной кучей.''&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Вдобавок к уже известным функциям и требованием иногда хочется уметь возвращаться к старой версии дерева. Таким образом, мы хотим добавить функцию checkout (version_number), где version_number - количество вызовов функции изменения дерева на подотрезке.&amp;lt;br&amp;gt;&lt;br /&gt;
Заметим, что при каждом вызове функции изменения дерева на подотрезке изменяется лишь O(log n) вершин.&lt;br /&gt;
&amp;lt;br&amp;gt; &lt;br /&gt;
[[Файл:ветвь.png]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Более того, при каждой такой операции изменяется лишь один путь от  корня до листа. Итак, мы может создавать новую версию этого пути, возвращая из функции change указатель на новую версию того самого пути. Тогда в каждой вершине на этом пути один из детей будет обновленным, а другой старым.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Имея на руках систему контроля версий, мы можем считать нашу функцию f от полуинтервала исходного массива не только в актуальной версии дерева, но и в любой его версии, имея массив, в котором индекс - номер версии, а значение - соответствующая подветвь.&lt;br /&gt;
&lt;br /&gt;
== Пример задачи ==&lt;br /&gt;
Имеется статический массив. Запрос - количество элементов с индексами из [L, R), которые лежат во множестве {x, x+1, ... y-1, y}. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
'''Способ 1.''' &amp;lt;br&amp;gt;&lt;br /&gt;
Будем в каждой вершине хранить весь соответсвующий массив в отсортированном виде. Кажется, что о памяти лучше не думать, но на самом деле все относительно прилично: каждый элемент находится от корня на расстоянии O(log n) вершин, поэтому всего дополнительно затрачено будет O(n log n). После этого необходимо найти наибольший элемент, меньший x и наименьший, больший y в массиве нужной вершины. Тогда нужно просто вернуть количество элементов между этими только что найденными. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
'''Способ 2.''' &amp;lt;br&amp;gt;&lt;br /&gt;
Отсортируем элементы по возрастанию (разумеется, с сохранением первоначальной позиции элементов), получив массив S. Создадим пустой массив того же размера E, сделаем на нем дерево отрезков. После этого будем пробегать по массиву S и ставить в массив E единицы в соответствии и сохраненным ранее порядком. После этого достаточно сделать count (L, R, &amp;quot;≤y&amp;quot;) - count (L, R, &amp;quot;&amp;lt;x&amp;quot;), используя описанную выше систему контроля версий. Памяти мы тратим по-прежнему O(n log n), поскольку у нас n разных ветвей по log(n) каждая.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''Замечание: можно сделать дерево отрезков из деревьев отрезков, чтобы эффективно отвечать на похожие запросы в случае плоскости. ''&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:%D0%92%D0%B5%D1%82%D0%B2%D1%8C.png&amp;diff=423</id>
		<title>Файл:Ветвь.png</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:%D0%92%D0%B5%D1%82%D0%B2%D1%8C.png&amp;diff=423"/>
				<updated>2020-05-15T09:53:43Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=%D0%94%D0%B5%D1%80%D0%B5%D0%B2%D0%BE_%D0%BE%D1%82%D1%80%D0%B5%D0%B7%D0%BA%D0%BE%D0%B2&amp;diff=422</id>
		<title>Дерево отрезков</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=%D0%94%D0%B5%D1%80%D0%B5%D0%B2%D0%BE_%D0%BE%D1%82%D1%80%D0%B5%D0%B7%D0%BA%D0%BE%D0%B2&amp;diff=422"/>
				<updated>2020-05-15T09:53:28Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: /* Персистентное дерево отрезков */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Общая идея ==&lt;br /&gt;
Очень похоже на sparse table - хотим предподсчитывать какие-то значения для отрезков, и обновлять их при изменении дерева.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
''Замечание: рассматриваем на примере задачи RMQ из конспекта про sparse table''&lt;br /&gt;
&lt;br /&gt;
== Структура ==&lt;br /&gt;
На нижнем уровне располагаются элементы массива, на уровень выше - узлы, в которых минимум из двух элементов, на уровень выше - минимум из соответствующих соседей предыдущего уровня и так далее. Рисунок для случая, когда количество элементов в массиве - степень двойки. &lt;br /&gt;
[[Файл:vis_tree.png]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Разумеется, уровней всего O(log n). На i-м уровне, начиная от самого нижнего, n / 2^i элементов. Таким образом, все дерево можно построить (просто сумма членов геометрической прогрессии) за O(n). &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Обработка запроса ==&lt;br /&gt;
Общая идея: процедура getMin начинает от корня дерева и рекурсивно спускается в те отрезки, которые ее интересуют. Пусть мы находимся в вершине x, которая соответствует полуинтервалу TreeSeg = [L_tree, R_tree), а нам нужен минимум на полуинтервале QuerySeg = [L_q, R_q). Строго говоря, возможны три случая:&amp;lt;br&amp;gt;&lt;br /&gt;
1. TreeSeg ⊆ QuerySeg. Тогда необходимо вернуть значение из вершины. &amp;lt;br&amp;gt;&lt;br /&gt;
2. TreeSeg ⋂ QuerySeg = ∅. Необходимо вернуть какую-то большую константу IntMax, или какой-то флаг, показывающий, что отрезки не пересекаются - чтобы уже &amp;quot;вышестоящая вершина&amp;quot; думала над тем, что с этим знанием делать. &amp;lt;br&amp;gt;&lt;br /&gt;
3. TreeSeg ⋂ QuerySeg ≠ ∅. Тогда, разумеется, для обработки запроса необходимо вернуть min (getMin (left_half (x)), getMin (right_half (x))). Заметим, что в силу того что left_half и right_half не пересекаются, мы в будущем сможем решать более сложные задачи с помощью этой же структуры.&lt;br /&gt;
&lt;br /&gt;
{{Template:Lemma&lt;br /&gt;
|What = Лемма 1&lt;br /&gt;
|Statement = Каждый такой запрос выполняется за O(log n). &lt;br /&gt;
|Proof = Понятно, что оценка асимптотики алгоритма сводится к подсчету количества рекурсий, которые наша процедура потребует для выполнения. Рассмотрим произвольный уровень нашего дерева при запросе. На нем мы сделаем не более двух рекурсий вниз по дереву (случай 3). Таким образом, количество рекурсий ограничено O(1) * O(height) = O(log n). &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Изменение дерева ==&lt;br /&gt;
''Замечание: на данном этапе предлагается перейти к произвольной функции f элементов на отрезке. Разумеется, нам подойдут не все функции. Необходимо, чтобы эта функция была ассоциативной, поскольку иначе мы не сможет &amp;quot;расставлять скобки&amp;quot; при сведении этой задачи к меньшим. ''&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
''Оговорка: мы храним наше дерево в виде массива - по аналогии с бинарной кучей. ''&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Итак, обрабатываем запрос вида change (i, new_value). Существуют два подхода:&amp;lt;br&amp;gt;&lt;br /&gt;
1. &amp;quot;Изменение снизу&amp;quot;. &amp;lt;br&amp;gt;&lt;br /&gt;
Зная номер вершины i мы можем пересчитать все значения дерева, которые идут на пути от корня до этой вершины (напомним, что индекс родителя при нашей оговорке мы найдем как i / 2). Эта схема плоха тем, что работает только для случая, когда количество элементов - степень двойки. &amp;lt;br&amp;gt;&lt;br /&gt;
2. &amp;quot;Изменение сверху&amp;quot;. &amp;lt;br&amp;gt;&lt;br /&gt;
Аналогично процедуре обработки запроса, мы рекурсивно спускаемся в нужное нам поддерево, переподсчитываем там нашу функцию, после этого обновляем значение в текущей вершины и возвращаем его. &lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Изменение элементов на подотрезке ==&lt;br /&gt;
Пусть нам поступает запрос на присвоение всем элементам с индексами от L до R значения new_value. Вновь применим идею отложенной операции. Будем рекурсивно спускаться от корня пока весь соответствующий текущей вершине полуинтервал не будет лежать в [L, R); когда это произошло, сохраним в этой вершине запрос на изменение. &amp;lt;br&amp;gt;&lt;br /&gt;
Тогда нужно понять, как это повлияет на процедуру  обработки запроса. Разумеется, изменится случай 1 - сейчас нам придется учитывать не только значение вершины, но и эту отложенную операцию, что, впрочем, несильно усложняет дело. Также необходимо в случае 3 &amp;quot;протолкнуть&amp;quot; отложенную операцию в сыновей. Понятно, что никакую асимптотику это нарушить не может. Но здесь важно не забыть после рассмотрения случаев 1, 2, 3 обновить значение в текущей вершине на основе вновь полученной информации о листьях данной вершины. И только после этого можно безопасно возвращаться к родительскому узлу.&lt;br /&gt;
&lt;br /&gt;
== Персистентное дерево отрезков ==&lt;br /&gt;
''Замечание: здесь нам придется отказаться от идеи хранить структуру в виде массива, по аналогии с бинарной кучей.''&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Вдобавок к уже известным функциям и требованием иногда хочется уметь возвращаться к старой версии дерева. Таким образом, мы хотим добавить функцию checkout (version_number), где version_number - количество вызовов функции изменения дерева на подотрезке.&amp;lt;br&amp;gt;&lt;br /&gt;
Заметим, что при каждом вызове функции изменения дерева на подотрезке изменяется лишь O(log n) вершин.&lt;br /&gt;
&amp;lt;br&amp;gt; &lt;br /&gt;
[[Файл:ветвь.png]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Более того, при каждой такой операции изменяется лишь один путь от  корня до листа. Итак, мы может создавать новую версию этого пути, возвращая из функции change указатель на новую версию того самого пути. Тогда в каждой вершине на этом пути один из детей будет обновленным, а другой старым.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Имея на руках систему контроля версий, мы можем считать нашу функцию f от полуинтервала исходного массива не только в актуальной версии дерева, но и в любой его версии, имея массив, в котором индекс - номер версии, а значение - соответствующая подветвь.&lt;br /&gt;
&lt;br /&gt;
== Пример задачи ==&lt;br /&gt;
Имеется статический массив. Запрос - количество элементов с индексами из [L, R), которые лежат во множестве {x, x+1, ... y-1, y}. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
'''Способ 1.''' &amp;lt;br&amp;gt;&lt;br /&gt;
Будем в каждой вершине хранить весь соответсвующий массив в отсортированном виде. Кажется, что о памяти лучше не думать, но на самом деле все относительно прилично: каждый элемент находится от корня на расстоянии O(log n) вершин, поэтому всего дополнительно затрачено будет O(n log n). После этого необходимо найти наибольший элемент, меньший x и наименьший, больший y в массиве нужной вершины. Тогда нужно просто вернуть количество элементов между этими только что найденными. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
'''Способ 2.''' &amp;lt;br&amp;gt;&lt;br /&gt;
Отсортирую элементы по возрастанию (разумеется, с сохранением первоначальной позиции), получив массив S. Создам пустой массив того же размера E, сделаю на нем дерево отрезков. После этого буду пробегать по массиву S и ставить в массив E единицы в соответствии и сохраненным ранее порядком. После этого мне достаточно сделать count (L, R, &amp;quot;&amp;lt;=y&amp;quot;) - count (L, R, &amp;quot;&amp;lt;x&amp;quot;), используя описанную выше систему контроля версий. Памяти мы тратим по-прежнему O(n log n), поскольку у нас n разных ветвей по log(n) каждая.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''Замечание: можно сделать дерево отрезков из деревьев отрезков, чтобы эффективно отвечать на похожие запросы в случае плоскости. ''&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=%D0%94%D0%B5%D1%80%D0%B5%D0%B2%D0%BE_%D0%BE%D1%82%D1%80%D0%B5%D0%B7%D0%BA%D0%BE%D0%B2&amp;diff=421</id>
		<title>Дерево отрезков</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=%D0%94%D0%B5%D1%80%D0%B5%D0%B2%D0%BE_%D0%BE%D1%82%D1%80%D0%B5%D0%B7%D0%BA%D0%BE%D0%B2&amp;diff=421"/>
				<updated>2020-05-15T09:51:39Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: /* Изменение элементов на подотрезке */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Общая идея ==&lt;br /&gt;
Очень похоже на sparse table - хотим предподсчитывать какие-то значения для отрезков, и обновлять их при изменении дерева.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
''Замечание: рассматриваем на примере задачи RMQ из конспекта про sparse table''&lt;br /&gt;
&lt;br /&gt;
== Структура ==&lt;br /&gt;
На нижнем уровне располагаются элементы массива, на уровень выше - узлы, в которых минимум из двух элементов, на уровень выше - минимум из соответствующих соседей предыдущего уровня и так далее. Рисунок для случая, когда количество элементов в массиве - степень двойки. &lt;br /&gt;
[[Файл:vis_tree.png]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Разумеется, уровней всего O(log n). На i-м уровне, начиная от самого нижнего, n / 2^i элементов. Таким образом, все дерево можно построить (просто сумма членов геометрической прогрессии) за O(n). &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Обработка запроса ==&lt;br /&gt;
Общая идея: процедура getMin начинает от корня дерева и рекурсивно спускается в те отрезки, которые ее интересуют. Пусть мы находимся в вершине x, которая соответствует полуинтервалу TreeSeg = [L_tree, R_tree), а нам нужен минимум на полуинтервале QuerySeg = [L_q, R_q). Строго говоря, возможны три случая:&amp;lt;br&amp;gt;&lt;br /&gt;
1. TreeSeg ⊆ QuerySeg. Тогда необходимо вернуть значение из вершины. &amp;lt;br&amp;gt;&lt;br /&gt;
2. TreeSeg ⋂ QuerySeg = ∅. Необходимо вернуть какую-то большую константу IntMax, или какой-то флаг, показывающий, что отрезки не пересекаются - чтобы уже &amp;quot;вышестоящая вершина&amp;quot; думала над тем, что с этим знанием делать. &amp;lt;br&amp;gt;&lt;br /&gt;
3. TreeSeg ⋂ QuerySeg ≠ ∅. Тогда, разумеется, для обработки запроса необходимо вернуть min (getMin (left_half (x)), getMin (right_half (x))). Заметим, что в силу того что left_half и right_half не пересекаются, мы в будущем сможем решать более сложные задачи с помощью этой же структуры.&lt;br /&gt;
&lt;br /&gt;
{{Template:Lemma&lt;br /&gt;
|What = Лемма 1&lt;br /&gt;
|Statement = Каждый такой запрос выполняется за O(log n). &lt;br /&gt;
|Proof = Понятно, что оценка асимптотики алгоритма сводится к подсчету количества рекурсий, которые наша процедура потребует для выполнения. Рассмотрим произвольный уровень нашего дерева при запросе. На нем мы сделаем не более двух рекурсий вниз по дереву (случай 3). Таким образом, количество рекурсий ограничено O(1) * O(height) = O(log n). &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Изменение дерева ==&lt;br /&gt;
''Замечание: на данном этапе предлагается перейти к произвольной функции f элементов на отрезке. Разумеется, нам подойдут не все функции. Необходимо, чтобы эта функция была ассоциативной, поскольку иначе мы не сможет &amp;quot;расставлять скобки&amp;quot; при сведении этой задачи к меньшим. ''&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
''Оговорка: мы храним наше дерево в виде массива - по аналогии с бинарной кучей. ''&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Итак, обрабатываем запрос вида change (i, new_value). Существуют два подхода:&amp;lt;br&amp;gt;&lt;br /&gt;
1. &amp;quot;Изменение снизу&amp;quot;. &amp;lt;br&amp;gt;&lt;br /&gt;
Зная номер вершины i мы можем пересчитать все значения дерева, которые идут на пути от корня до этой вершины (напомним, что индекс родителя при нашей оговорке мы найдем как i / 2). Эта схема плоха тем, что работает только для случая, когда количество элементов - степень двойки. &amp;lt;br&amp;gt;&lt;br /&gt;
2. &amp;quot;Изменение сверху&amp;quot;. &amp;lt;br&amp;gt;&lt;br /&gt;
Аналогично процедуре обработки запроса, мы рекурсивно спускаемся в нужное нам поддерево, переподсчитываем там нашу функцию, после этого обновляем значение в текущей вершины и возвращаем его. &lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Изменение элементов на подотрезке ==&lt;br /&gt;
Пусть нам поступает запрос на присвоение всем элементам с индексами от L до R значения new_value. Вновь применим идею отложенной операции. Будем рекурсивно спускаться от корня пока весь соответствующий текущей вершине полуинтервал не будет лежать в [L, R); когда это произошло, сохраним в этой вершине запрос на изменение. &amp;lt;br&amp;gt;&lt;br /&gt;
Тогда нужно понять, как это повлияет на процедуру  обработки запроса. Разумеется, изменится случай 1 - сейчас нам придется учитывать не только значение вершины, но и эту отложенную операцию, что, впрочем, несильно усложняет дело. Также необходимо в случае 3 &amp;quot;протолкнуть&amp;quot; отложенную операцию в сыновей. Понятно, что никакую асимптотику это нарушить не может. Но здесь важно не забыть после рассмотрения случаев 1, 2, 3 обновить значение в текущей вершине на основе вновь полученной информации о листьях данной вершины. И только после этого можно безопасно возвращаться к родительскому узлу.&lt;br /&gt;
&lt;br /&gt;
== Персистентное дерево отрезков ==&lt;br /&gt;
''Замечание: здесь нам придется отказаться от идеи хранить структуру в виде массива, по аналогии с бинарной кучей.''&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Вдобавок к уже известным функциям и требованием иногда хочется уметь возвращаться к старой версии дерева. Таким образом, мы хотим добавить функцию checkout (version_number), где version_number - количество вызовов функции изменения дерева на подотрезке.&amp;lt;br&amp;gt;&lt;br /&gt;
Заметим, что при каждом вызове функции изменения дерева на подотрезке изменяется лишь O(log n) вершин. (vis 56:46). Более того, при каждой такой операции изменяется лишь один путь от  корня до листа. Итак, мы может создавать новую версию этого пути, возвращая из функции change указатель на новую версию того самого пути. Тогда в каждой вершине на этом пути один из детей будет обновленным, а другой старым.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Имея на руках систему контроля версий, мы можем считать нашу функцию f от полуинтервала исходного массива не только в актуальной версии дерева, но и в любой его версии, имея массив, в котором индекс - номер версии, а значение - соответствующая подветвь.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пример задачи ==&lt;br /&gt;
Имеется статический массив. Запрос - количество элементов с индексами из [L, R), которые лежат во множестве {x, x+1, ... y-1, y}. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
'''Способ 1.''' &amp;lt;br&amp;gt;&lt;br /&gt;
Будем в каждой вершине хранить весь соответсвующий массив в отсортированном виде. Кажется, что о памяти лучше не думать, но на самом деле все относительно прилично: каждый элемент находится от корня на расстоянии O(log n) вершин, поэтому всего дополнительно затрачено будет O(n log n). После этого необходимо найти наибольший элемент, меньший x и наименьший, больший y в массиве нужной вершины. Тогда нужно просто вернуть количество элементов между этими только что найденными. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
'''Способ 2.''' &amp;lt;br&amp;gt;&lt;br /&gt;
Отсортирую элементы по возрастанию (разумеется, с сохранением первоначальной позиции), получив массив S. Создам пустой массив того же размера E, сделаю на нем дерево отрезков. После этого буду пробегать по массиву S и ставить в массив E единицы в соответствии и сохраненным ранее порядком. После этого мне достаточно сделать count (L, R, &amp;quot;&amp;lt;=y&amp;quot;) - count (L, R, &amp;quot;&amp;lt;x&amp;quot;), используя описанную выше систему контроля версий. Памяти мы тратим по-прежнему O(n log n), поскольку у нас n разных ветвей по log(n) каждая.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''Замечание: можно сделать дерево отрезков из деревьев отрезков, чтобы эффективно отвечать на похожие запросы в случае плоскости. ''&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=%D0%94%D0%B5%D1%80%D0%B5%D0%B2%D0%BE_%D0%BE%D1%82%D1%80%D0%B5%D0%B7%D0%BA%D0%BE%D0%B2&amp;diff=420</id>
		<title>Дерево отрезков</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=%D0%94%D0%B5%D1%80%D0%B5%D0%B2%D0%BE_%D0%BE%D1%82%D1%80%D0%B5%D0%B7%D0%BA%D0%BE%D0%B2&amp;diff=420"/>
				<updated>2020-05-15T09:49:31Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: /* Обработка запроса */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Общая идея ==&lt;br /&gt;
Очень похоже на sparse table - хотим предподсчитывать какие-то значения для отрезков, и обновлять их при изменении дерева.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
''Замечание: рассматриваем на примере задачи RMQ из конспекта про sparse table''&lt;br /&gt;
&lt;br /&gt;
== Структура ==&lt;br /&gt;
На нижнем уровне располагаются элементы массива, на уровень выше - узлы, в которых минимум из двух элементов, на уровень выше - минимум из соответствующих соседей предыдущего уровня и так далее. Рисунок для случая, когда количество элементов в массиве - степень двойки. &lt;br /&gt;
[[Файл:vis_tree.png]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Разумеется, уровней всего O(log n). На i-м уровне, начиная от самого нижнего, n / 2^i элементов. Таким образом, все дерево можно построить (просто сумма членов геометрической прогрессии) за O(n). &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Обработка запроса ==&lt;br /&gt;
Общая идея: процедура getMin начинает от корня дерева и рекурсивно спускается в те отрезки, которые ее интересуют. Пусть мы находимся в вершине x, которая соответствует полуинтервалу TreeSeg = [L_tree, R_tree), а нам нужен минимум на полуинтервале QuerySeg = [L_q, R_q). Строго говоря, возможны три случая:&amp;lt;br&amp;gt;&lt;br /&gt;
1. TreeSeg ⊆ QuerySeg. Тогда необходимо вернуть значение из вершины. &amp;lt;br&amp;gt;&lt;br /&gt;
2. TreeSeg ⋂ QuerySeg = ∅. Необходимо вернуть какую-то большую константу IntMax, или какой-то флаг, показывающий, что отрезки не пересекаются - чтобы уже &amp;quot;вышестоящая вершина&amp;quot; думала над тем, что с этим знанием делать. &amp;lt;br&amp;gt;&lt;br /&gt;
3. TreeSeg ⋂ QuerySeg ≠ ∅. Тогда, разумеется, для обработки запроса необходимо вернуть min (getMin (left_half (x)), getMin (right_half (x))). Заметим, что в силу того что left_half и right_half не пересекаются, мы в будущем сможем решать более сложные задачи с помощью этой же структуры.&lt;br /&gt;
&lt;br /&gt;
{{Template:Lemma&lt;br /&gt;
|What = Лемма 1&lt;br /&gt;
|Statement = Каждый такой запрос выполняется за O(log n). &lt;br /&gt;
|Proof = Понятно, что оценка асимптотики алгоритма сводится к подсчету количества рекурсий, которые наша процедура потребует для выполнения. Рассмотрим произвольный уровень нашего дерева при запросе. На нем мы сделаем не более двух рекурсий вниз по дереву (случай 3). Таким образом, количество рекурсий ограничено O(1) * O(height) = O(log n). &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Изменение дерева ==&lt;br /&gt;
''Замечание: на данном этапе предлагается перейти к произвольной функции f элементов на отрезке. Разумеется, нам подойдут не все функции. Необходимо, чтобы эта функция была ассоциативной, поскольку иначе мы не сможет &amp;quot;расставлять скобки&amp;quot; при сведении этой задачи к меньшим. ''&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
''Оговорка: мы храним наше дерево в виде массива - по аналогии с бинарной кучей. ''&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Итак, обрабатываем запрос вида change (i, new_value). Существуют два подхода:&amp;lt;br&amp;gt;&lt;br /&gt;
1. &amp;quot;Изменение снизу&amp;quot;. &amp;lt;br&amp;gt;&lt;br /&gt;
Зная номер вершины i мы можем пересчитать все значения дерева, которые идут на пути от корня до этой вершины (напомним, что индекс родителя при нашей оговорке мы найдем как i / 2). Эта схема плоха тем, что работает только для случая, когда количество элементов - степень двойки. &amp;lt;br&amp;gt;&lt;br /&gt;
2. &amp;quot;Изменение сверху&amp;quot;. &amp;lt;br&amp;gt;&lt;br /&gt;
Аналогично процедуре обработки запроса, мы рекурсивно спускаемся в нужное нам поддерево, переподсчитываем там нашу функцию, после этого обновляем значение в текущей вершины и возвращаем его. &lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Изменение элементов на подотрезке ==&lt;br /&gt;
Пусть нам поступает запрос на присвоение всем элементам с индексами от L до R значения new_value. Вновь применим идею отложенной операции. Будем рекурсивно спускаться от корня пока весь соответствующий текущей вершине полуинтервал не будет лежать в [L, R); когда это произошло, сохраним в этой вершине запрос на изменение. &amp;lt;br&amp;gt;&lt;br /&gt;
Тогда нужно понять, как это повлияет на процедуру  обработки запроса. Разумеется, изменится случай 1 - сейчас нам придется учитывать не только значение вершины, но и эту отложенную операцию, что, впрочем, несильно усложняет дело. Также необходимо в случае 3 &amp;quot;протолкнуть&amp;quot; отложенную операцию в сыновей. Понятно, что никакую асимптотику это нарушить не может. Но здесь важно не забыть после рассмотрения случаев 1, 2, 3 обновить значение в текущей вершине на основе обновленной информации о детях и той ленивой метке, которая сейчас есть. И только после этого можно безопасно возвращаться на уровень выше.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Персистентное дерево отрезков ==&lt;br /&gt;
''Замечание: здесь нам придется отказаться от идеи хранить структуру в виде массива, по аналогии с бинарной кучей.''&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Вдобавок к уже известным функциям и требованием иногда хочется уметь возвращаться к старой версии дерева. Таким образом, мы хотим добавить функцию checkout (version_number), где version_number - количество вызовов функции изменения дерева на подотрезке.&amp;lt;br&amp;gt;&lt;br /&gt;
Заметим, что при каждом вызове функции изменения дерева на подотрезке изменяется лишь O(log n) вершин. (vis 56:46). Более того, при каждой такой операции изменяется лишь один путь от  корня до листа. Итак, мы может создавать новую версию этого пути, возвращая из функции change указатель на новую версию того самого пути. Тогда в каждой вершине на этом пути один из детей будет обновленным, а другой старым.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Имея на руках систему контроля версий, мы можем считать нашу функцию f от полуинтервала исходного массива не только в актуальной версии дерева, но и в любой его версии, имея массив, в котором индекс - номер версии, а значение - соответствующая подветвь.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пример задачи ==&lt;br /&gt;
Имеется статический массив. Запрос - количество элементов с индексами из [L, R), которые лежат во множестве {x, x+1, ... y-1, y}. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
'''Способ 1.''' &amp;lt;br&amp;gt;&lt;br /&gt;
Будем в каждой вершине хранить весь соответсвующий массив в отсортированном виде. Кажется, что о памяти лучше не думать, но на самом деле все относительно прилично: каждый элемент находится от корня на расстоянии O(log n) вершин, поэтому всего дополнительно затрачено будет O(n log n). После этого необходимо найти наибольший элемент, меньший x и наименьший, больший y в массиве нужной вершины. Тогда нужно просто вернуть количество элементов между этими только что найденными. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
'''Способ 2.''' &amp;lt;br&amp;gt;&lt;br /&gt;
Отсортирую элементы по возрастанию (разумеется, с сохранением первоначальной позиции), получив массив S. Создам пустой массив того же размера E, сделаю на нем дерево отрезков. После этого буду пробегать по массиву S и ставить в массив E единицы в соответствии и сохраненным ранее порядком. После этого мне достаточно сделать count (L, R, &amp;quot;&amp;lt;=y&amp;quot;) - count (L, R, &amp;quot;&amp;lt;x&amp;quot;), используя описанную выше систему контроля версий. Памяти мы тратим по-прежнему O(n log n), поскольку у нас n разных ветвей по log(n) каждая.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''Замечание: можно сделать дерево отрезков из деревьев отрезков, чтобы эффективно отвечать на похожие запросы в случае плоскости. ''&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=%D0%94%D0%B5%D1%80%D0%B5%D0%B2%D0%BE_%D0%BE%D1%82%D1%80%D0%B5%D0%B7%D0%BA%D0%BE%D0%B2&amp;diff=419</id>
		<title>Дерево отрезков</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=%D0%94%D0%B5%D1%80%D0%B5%D0%B2%D0%BE_%D0%BE%D1%82%D1%80%D0%B5%D0%B7%D0%BA%D0%BE%D0%B2&amp;diff=419"/>
				<updated>2020-05-15T09:48:56Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: /* Обработка запроса */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Общая идея ==&lt;br /&gt;
Очень похоже на sparse table - хотим предподсчитывать какие-то значения для отрезков, и обновлять их при изменении дерева.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
''Замечание: рассматриваем на примере задачи RMQ из конспекта про sparse table''&lt;br /&gt;
&lt;br /&gt;
== Структура ==&lt;br /&gt;
На нижнем уровне располагаются элементы массива, на уровень выше - узлы, в которых минимум из двух элементов, на уровень выше - минимум из соответствующих соседей предыдущего уровня и так далее. Рисунок для случая, когда количество элементов в массиве - степень двойки. &lt;br /&gt;
[[Файл:vis_tree.png]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Разумеется, уровней всего O(log n). На i-м уровне, начиная от самого нижнего, n / 2^i элементов. Таким образом, все дерево можно построить (просто сумма членов геометрической прогрессии) за O(n). &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Обработка запроса ==&lt;br /&gt;
Общая идея: процедура getMin начинает от корня дерева и рекурсивно спускается в те отрезки, которые ее интересуют. Пусть мы находимся в вершине x, которая соответствует полуинтервалу TreeSeg = [L_tree, R_tree), а нам нужен минимум на полуинтервале QuerySeg = [L_q, R_q). Строго говоря, возможны три случая:&amp;lt;br&amp;gt;&lt;br /&gt;
1. TreeSeg ⊆ QuerySeg. Тогда необходимо вернуть значение из вершины. &amp;lt;br&amp;gt;&lt;br /&gt;
2. TreeSeg ⋂ QuerySeg = ∅. Необходимо вернуть какую-то большую константу IntMax, или какой-то флаг, показывающий, что отрезки не пересекаются - чтобы уже &amp;quot;вышестоящий орган&amp;quot; думал над тем, что с этим знанием делать. &amp;lt;br&amp;gt;&lt;br /&gt;
3. TreeSeg ⋂ QuerySeg ≠ ∅. Тогда, разумеется, для обработки запроса необходимо вернуть min (getMin (left_half (x)), getMin (right_half (x))). Заметим, что в силу того что left_half и right_half не пересекаются, мы в будущем сможем решать более сложные задачи с помощью этой же структуры.&lt;br /&gt;
&lt;br /&gt;
{{Template:Lemma&lt;br /&gt;
|What = Лемма 1&lt;br /&gt;
|Statement = Каждый такой запрос выполняется за O(log n). &lt;br /&gt;
|Proof = Понятно, что оценка асимптотики алгоритма сводится к подсчету количества рекурсий, которые наша процедура потребует для выполнения. Рассмотрим произвольный уровень нашего дерева при запросе. На нем мы сделаем не более двух рекурсий вниз по дереву (случай 3). Таким образом, количество рекурсий ограничено O(1) * O(height) = O(log n). &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Изменение дерева ==&lt;br /&gt;
''Замечание: на данном этапе предлагается перейти к произвольной функции f элементов на отрезке. Разумеется, нам подойдут не все функции. Необходимо, чтобы эта функция была ассоциативной, поскольку иначе мы не сможет &amp;quot;расставлять скобки&amp;quot; при сведении этой задачи к меньшим. ''&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
''Оговорка: мы храним наше дерево в виде массива - по аналогии с бинарной кучей. ''&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Итак, обрабатываем запрос вида change (i, new_value). Существуют два подхода:&amp;lt;br&amp;gt;&lt;br /&gt;
1. &amp;quot;Изменение снизу&amp;quot;. &amp;lt;br&amp;gt;&lt;br /&gt;
Зная номер вершины i мы можем пересчитать все значения дерева, которые идут на пути от корня до этой вершины (напомним, что индекс родителя при нашей оговорке мы найдем как i / 2). Эта схема плоха тем, что работает только для случая, когда количество элементов - степень двойки. &amp;lt;br&amp;gt;&lt;br /&gt;
2. &amp;quot;Изменение сверху&amp;quot;. &amp;lt;br&amp;gt;&lt;br /&gt;
Аналогично процедуре обработки запроса, мы рекурсивно спускаемся в нужное нам поддерево, переподсчитываем там нашу функцию, после этого обновляем значение в текущей вершины и возвращаем его. &lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Изменение элементов на подотрезке ==&lt;br /&gt;
Пусть нам поступает запрос на присвоение всем элементам с индексами от L до R значения new_value. Вновь применим идею отложенной операции. Будем рекурсивно спускаться от корня пока весь соответствующий текущей вершине полуинтервал не будет лежать в [L, R); когда это произошло, сохраним в этой вершине запрос на изменение. &amp;lt;br&amp;gt;&lt;br /&gt;
Тогда нужно понять, как это повлияет на процедуру  обработки запроса. Разумеется, изменится случай 1 - сейчас нам придется учитывать не только значение вершины, но и эту отложенную операцию, что, впрочем, несильно усложняет дело. Также необходимо в случае 3 &amp;quot;протолкнуть&amp;quot; отложенную операцию в сыновей. Понятно, что никакую асимптотику это нарушить не может. Но здесь важно не забыть после рассмотрения случаев 1, 2, 3 обновить значение в текущей вершине на основе обновленной информации о детях и той ленивой метке, которая сейчас есть. И только после этого можно безопасно возвращаться на уровень выше.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Персистентное дерево отрезков ==&lt;br /&gt;
''Замечание: здесь нам придется отказаться от идеи хранить структуру в виде массива, по аналогии с бинарной кучей.''&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Вдобавок к уже известным функциям и требованием иногда хочется уметь возвращаться к старой версии дерева. Таким образом, мы хотим добавить функцию checkout (version_number), где version_number - количество вызовов функции изменения дерева на подотрезке.&amp;lt;br&amp;gt;&lt;br /&gt;
Заметим, что при каждом вызове функции изменения дерева на подотрезке изменяется лишь O(log n) вершин. (vis 56:46). Более того, при каждой такой операции изменяется лишь один путь от  корня до листа. Итак, мы может создавать новую версию этого пути, возвращая из функции change указатель на новую версию того самого пути. Тогда в каждой вершине на этом пути один из детей будет обновленным, а другой старым.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Имея на руках систему контроля версий, мы можем считать нашу функцию f от полуинтервала исходного массива не только в актуальной версии дерева, но и в любой его версии, имея массив, в котором индекс - номер версии, а значение - соответствующая подветвь.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пример задачи ==&lt;br /&gt;
Имеется статический массив. Запрос - количество элементов с индексами из [L, R), которые лежат во множестве {x, x+1, ... y-1, y}. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
'''Способ 1.''' &amp;lt;br&amp;gt;&lt;br /&gt;
Будем в каждой вершине хранить весь соответсвующий массив в отсортированном виде. Кажется, что о памяти лучше не думать, но на самом деле все относительно прилично: каждый элемент находится от корня на расстоянии O(log n) вершин, поэтому всего дополнительно затрачено будет O(n log n). После этого необходимо найти наибольший элемент, меньший x и наименьший, больший y в массиве нужной вершины. Тогда нужно просто вернуть количество элементов между этими только что найденными. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
'''Способ 2.''' &amp;lt;br&amp;gt;&lt;br /&gt;
Отсортирую элементы по возрастанию (разумеется, с сохранением первоначальной позиции), получив массив S. Создам пустой массив того же размера E, сделаю на нем дерево отрезков. После этого буду пробегать по массиву S и ставить в массив E единицы в соответствии и сохраненным ранее порядком. После этого мне достаточно сделать count (L, R, &amp;quot;&amp;lt;=y&amp;quot;) - count (L, R, &amp;quot;&amp;lt;x&amp;quot;), используя описанную выше систему контроля версий. Памяти мы тратим по-прежнему O(n log n), поскольку у нас n разных ветвей по log(n) каждая.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''Замечание: можно сделать дерево отрезков из деревьев отрезков, чтобы эффективно отвечать на похожие запросы в случае плоскости. ''&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=%D0%94%D0%B5%D1%80%D0%B5%D0%B2%D0%BE_%D0%BE%D1%82%D1%80%D0%B5%D0%B7%D0%BA%D0%BE%D0%B2&amp;diff=418</id>
		<title>Дерево отрезков</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=%D0%94%D0%B5%D1%80%D0%B5%D0%B2%D0%BE_%D0%BE%D1%82%D1%80%D0%B5%D0%B7%D0%BA%D0%BE%D0%B2&amp;diff=418"/>
				<updated>2020-05-15T09:48:06Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: /* Обработка запроса */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Общая идея ==&lt;br /&gt;
Очень похоже на sparse table - хотим предподсчитывать какие-то значения для отрезков, и обновлять их при изменении дерева.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
''Замечание: рассматриваем на примере задачи RMQ из конспекта про sparse table''&lt;br /&gt;
&lt;br /&gt;
== Структура ==&lt;br /&gt;
На нижнем уровне располагаются элементы массива, на уровень выше - узлы, в которых минимум из двух элементов, на уровень выше - минимум из соответствующих соседей предыдущего уровня и так далее. Рисунок для случая, когда количество элементов в массиве - степень двойки. &lt;br /&gt;
[[Файл:vis_tree.png]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Разумеется, уровней всего O(log n). На i-м уровне, начиная от самого нижнего, n / 2^i элементов. Таким образом, все дерево можно построить (просто сумма членов геометрической прогрессии) за O(n). &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Обработка запроса ==&lt;br /&gt;
Общая идея: процедура getMin начинает от корня дерева и рекурсивно спускается в те отрезки, которые ее интересуют. Пусть мы находимся в вершине x, которая соответствует полуинтервалу TreeSeg = [L_tree, R_tree), а нам нужен минимум на полуинтервале QuerySeg = [L_q, R_q). Строго говоря, возможны три случая:&amp;lt;br&amp;gt;&lt;br /&gt;
1. TreeSeg ⊆ QuerySeg. Тогда необходимо вернуть значение из вершины. &amp;lt;br&amp;gt;&lt;br /&gt;
2. TreeSeg ⋂ QuerySeg = ∅. Необходимо вернуть какую-то большую константу IntMax, или какой-то флаг, показывающий, что отрезки не пересекаются - чтобы уже &amp;quot;вышестоящий орган&amp;quot; думал над тем, что с этим знанием делать. &amp;lt;br&amp;gt;&lt;br /&gt;
3. TreeSeg ⋂ QuerySe != ∅. Тогда, разумеется, для обработки запроса необходимо вернуть min (getMin (left_half (x)), getMin (right_half (x))). Заметим, что в силу того что left_half и right_half не пересекаются, мы в будущем сможем решать более сложные задачи с помощью этой же структуры.&lt;br /&gt;
&lt;br /&gt;
{{Template:Lemma&lt;br /&gt;
|What = Лемма 1&lt;br /&gt;
|Statement = Каждый такой запрос выполняется за O(log n). &lt;br /&gt;
|Proof = Понятно, что оценка асимптотики алгоритма сводится к подсчету количества рекурсий, которые наша процедура потребует для выполнения. Рассмотрим произвольный уровень нашего дерева при запросе. На нем мы сделаем не более двух рекурсий вниз по дереву (случай 3). Таким образом, количество рекурсий ограничено O(1) * O(height) = O(log n). &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Изменение дерева ==&lt;br /&gt;
''Замечание: на данном этапе предлагается перейти к произвольной функции f элементов на отрезке. Разумеется, нам подойдут не все функции. Необходимо, чтобы эта функция была ассоциативной, поскольку иначе мы не сможет &amp;quot;расставлять скобки&amp;quot; при сведении этой задачи к меньшим. ''&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
''Оговорка: мы храним наше дерево в виде массива - по аналогии с бинарной кучей. ''&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Итак, обрабатываем запрос вида change (i, new_value). Существуют два подхода:&amp;lt;br&amp;gt;&lt;br /&gt;
1. &amp;quot;Изменение снизу&amp;quot;. &amp;lt;br&amp;gt;&lt;br /&gt;
Зная номер вершины i мы можем пересчитать все значения дерева, которые идут на пути от корня до этой вершины (напомним, что индекс родителя при нашей оговорке мы найдем как i / 2). Эта схема плоха тем, что работает только для случая, когда количество элементов - степень двойки. &amp;lt;br&amp;gt;&lt;br /&gt;
2. &amp;quot;Изменение сверху&amp;quot;. &amp;lt;br&amp;gt;&lt;br /&gt;
Аналогично процедуре обработки запроса, мы рекурсивно спускаемся в нужное нам поддерево, переподсчитываем там нашу функцию, после этого обновляем значение в текущей вершины и возвращаем его. &lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Изменение элементов на подотрезке ==&lt;br /&gt;
Пусть нам поступает запрос на присвоение всем элементам с индексами от L до R значения new_value. Вновь применим идею отложенной операции. Будем рекурсивно спускаться от корня пока весь соответствующий текущей вершине полуинтервал не будет лежать в [L, R); когда это произошло, сохраним в этой вершине запрос на изменение. &amp;lt;br&amp;gt;&lt;br /&gt;
Тогда нужно понять, как это повлияет на процедуру  обработки запроса. Разумеется, изменится случай 1 - сейчас нам придется учитывать не только значение вершины, но и эту отложенную операцию, что, впрочем, несильно усложняет дело. Также необходимо в случае 3 &amp;quot;протолкнуть&amp;quot; отложенную операцию в сыновей. Понятно, что никакую асимптотику это нарушить не может. Но здесь важно не забыть после рассмотрения случаев 1, 2, 3 обновить значение в текущей вершине на основе обновленной информации о детях и той ленивой метке, которая сейчас есть. И только после этого можно безопасно возвращаться на уровень выше.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Персистентное дерево отрезков ==&lt;br /&gt;
''Замечание: здесь нам придется отказаться от идеи хранить структуру в виде массива, по аналогии с бинарной кучей.''&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Вдобавок к уже известным функциям и требованием иногда хочется уметь возвращаться к старой версии дерева. Таким образом, мы хотим добавить функцию checkout (version_number), где version_number - количество вызовов функции изменения дерева на подотрезке.&amp;lt;br&amp;gt;&lt;br /&gt;
Заметим, что при каждом вызове функции изменения дерева на подотрезке изменяется лишь O(log n) вершин. (vis 56:46). Более того, при каждой такой операции изменяется лишь один путь от  корня до листа. Итак, мы может создавать новую версию этого пути, возвращая из функции change указатель на новую версию того самого пути. Тогда в каждой вершине на этом пути один из детей будет обновленным, а другой старым.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Имея на руках систему контроля версий, мы можем считать нашу функцию f от полуинтервала исходного массива не только в актуальной версии дерева, но и в любой его версии, имея массив, в котором индекс - номер версии, а значение - соответствующая подветвь.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пример задачи ==&lt;br /&gt;
Имеется статический массив. Запрос - количество элементов с индексами из [L, R), которые лежат во множестве {x, x+1, ... y-1, y}. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
'''Способ 1.''' &amp;lt;br&amp;gt;&lt;br /&gt;
Будем в каждой вершине хранить весь соответсвующий массив в отсортированном виде. Кажется, что о памяти лучше не думать, но на самом деле все относительно прилично: каждый элемент находится от корня на расстоянии O(log n) вершин, поэтому всего дополнительно затрачено будет O(n log n). После этого необходимо найти наибольший элемент, меньший x и наименьший, больший y в массиве нужной вершины. Тогда нужно просто вернуть количество элементов между этими только что найденными. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
'''Способ 2.''' &amp;lt;br&amp;gt;&lt;br /&gt;
Отсортирую элементы по возрастанию (разумеется, с сохранением первоначальной позиции), получив массив S. Создам пустой массив того же размера E, сделаю на нем дерево отрезков. После этого буду пробегать по массиву S и ставить в массив E единицы в соответствии и сохраненным ранее порядком. После этого мне достаточно сделать count (L, R, &amp;quot;&amp;lt;=y&amp;quot;) - count (L, R, &amp;quot;&amp;lt;x&amp;quot;), используя описанную выше систему контроля версий. Памяти мы тратим по-прежнему O(n log n), поскольку у нас n разных ветвей по log(n) каждая.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''Замечание: можно сделать дерево отрезков из деревьев отрезков, чтобы эффективно отвечать на похожие запросы в случае плоскости. ''&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=%D0%94%D0%B5%D1%80%D0%B5%D0%B2%D0%BE_%D0%BE%D1%82%D1%80%D0%B5%D0%B7%D0%BA%D0%BE%D0%B2&amp;diff=417</id>
		<title>Дерево отрезков</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=%D0%94%D0%B5%D1%80%D0%B5%D0%B2%D0%BE_%D0%BE%D1%82%D1%80%D0%B5%D0%B7%D0%BA%D0%BE%D0%B2&amp;diff=417"/>
				<updated>2020-05-15T09:46:45Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: /* Обработка запроса */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Общая идея ==&lt;br /&gt;
Очень похоже на sparse table - хотим предподсчитывать какие-то значения для отрезков, и обновлять их при изменении дерева.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
''Замечание: рассматриваем на примере задачи RMQ из конспекта про sparse table''&lt;br /&gt;
&lt;br /&gt;
== Структура ==&lt;br /&gt;
На нижнем уровне располагаются элементы массива, на уровень выше - узлы, в которых минимум из двух элементов, на уровень выше - минимум из соответствующих соседей предыдущего уровня и так далее. Рисунок для случая, когда количество элементов в массиве - степень двойки. &lt;br /&gt;
[[Файл:vis_tree.png]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Разумеется, уровней всего O(log n). На i-м уровне, начиная от самого нижнего, n / 2^i элементов. Таким образом, все дерево можно построить (просто сумма членов геометрической прогрессии) за O(n). &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Обработка запроса ==&lt;br /&gt;
Общая идея: процедура getMin начинает от корня дерева и рекурсивно спускается в те отрезки, которые ее интересуют. Пусть мы находимся в вершине x, которая соответствует полуинтервалу TreeSeg = [L_tree, R_tree), а нам нужен минимум на полуинтервале QuerySeg = [L_q, R_q). Строго говоря, возможны три случая:&amp;lt;br&amp;gt;&lt;br /&gt;
1. TreeSeg &amp;lt; QuerySeg. Тогда необходимо вернуть значение из вершины. &amp;lt;br&amp;gt;&lt;br /&gt;
2. TreeSeg () QuerySeg = ∅. Необходимо вернуть какую-то большую константу IntMax, или какой-то флаг, показывающий, что отрезки не пересекаются - чтобы уже &amp;quot;вышестоящий орган&amp;quot; думал над тем, что с этим знанием делать. &amp;lt;br&amp;gt;&lt;br /&gt;
3. TreeSeg () QuerySe != ∅. Тогда, разумеется, для обработки запроса необходимо вернуть min (getMin (left_half (x)), getMin (right_half (x))). Заметим, что в силу того что left_half и right_half не пересекаются, мы в будущем сможем решать более сложные задачи с помощью этой же структуры.&lt;br /&gt;
&lt;br /&gt;
{{Template:Lemma&lt;br /&gt;
|What = Лемма 1&lt;br /&gt;
|Statement = Каждый такой запрос выполняется за O(log n). &lt;br /&gt;
|Proof = Понятно, что оценка асимптотики алгоритма сводится к подсчету количества рекурсий, которые наша процедура потребует для выполнения. Рассмотрим произвольный уровень нашего дерева при запросе. На нем мы сделаем не более двух рекурсий вниз по дереву (случай 3). Таким образом, количество рекурсий ограничено O(1) * O(height) = O(log n). &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Изменение дерева ==&lt;br /&gt;
''Замечание: на данном этапе предлагается перейти к произвольной функции f элементов на отрезке. Разумеется, нам подойдут не все функции. Необходимо, чтобы эта функция была ассоциативной, поскольку иначе мы не сможет &amp;quot;расставлять скобки&amp;quot; при сведении этой задачи к меньшим. ''&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
''Оговорка: мы храним наше дерево в виде массива - по аналогии с бинарной кучей. ''&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Итак, обрабатываем запрос вида change (i, new_value). Существуют два подхода:&amp;lt;br&amp;gt;&lt;br /&gt;
1. &amp;quot;Изменение снизу&amp;quot;. &amp;lt;br&amp;gt;&lt;br /&gt;
Зная номер вершины i мы можем пересчитать все значения дерева, которые идут на пути от корня до этой вершины (напомним, что индекс родителя при нашей оговорке мы найдем как i / 2). Эта схема плоха тем, что работает только для случая, когда количество элементов - степень двойки. &amp;lt;br&amp;gt;&lt;br /&gt;
2. &amp;quot;Изменение сверху&amp;quot;. &amp;lt;br&amp;gt;&lt;br /&gt;
Аналогично процедуре обработки запроса, мы рекурсивно спускаемся в нужное нам поддерево, переподсчитываем там нашу функцию, после этого обновляем значение в текущей вершины и возвращаем его. &lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Изменение элементов на подотрезке ==&lt;br /&gt;
Пусть нам поступает запрос на присвоение всем элементам с индексами от L до R значения new_value. Вновь применим идею отложенной операции. Будем рекурсивно спускаться от корня пока весь соответствующий текущей вершине полуинтервал не будет лежать в [L, R); когда это произошло, сохраним в этой вершине запрос на изменение. &amp;lt;br&amp;gt;&lt;br /&gt;
Тогда нужно понять, как это повлияет на процедуру  обработки запроса. Разумеется, изменится случай 1 - сейчас нам придется учитывать не только значение вершины, но и эту отложенную операцию, что, впрочем, несильно усложняет дело. Также необходимо в случае 3 &amp;quot;протолкнуть&amp;quot; отложенную операцию в сыновей. Понятно, что никакую асимптотику это нарушить не может. Но здесь важно не забыть после рассмотрения случаев 1, 2, 3 обновить значение в текущей вершине на основе обновленной информации о детях и той ленивой метке, которая сейчас есть. И только после этого можно безопасно возвращаться на уровень выше.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Персистентное дерево отрезков ==&lt;br /&gt;
''Замечание: здесь нам придется отказаться от идеи хранить структуру в виде массива, по аналогии с бинарной кучей.''&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Вдобавок к уже известным функциям и требованием иногда хочется уметь возвращаться к старой версии дерева. Таким образом, мы хотим добавить функцию checkout (version_number), где version_number - количество вызовов функции изменения дерева на подотрезке.&amp;lt;br&amp;gt;&lt;br /&gt;
Заметим, что при каждом вызове функции изменения дерева на подотрезке изменяется лишь O(log n) вершин. (vis 56:46). Более того, при каждой такой операции изменяется лишь один путь от  корня до листа. Итак, мы может создавать новую версию этого пути, возвращая из функции change указатель на новую версию того самого пути. Тогда в каждой вершине на этом пути один из детей будет обновленным, а другой старым.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Имея на руках систему контроля версий, мы можем считать нашу функцию f от полуинтервала исходного массива не только в актуальной версии дерева, но и в любой его версии, имея массив, в котором индекс - номер версии, а значение - соответствующая подветвь.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пример задачи ==&lt;br /&gt;
Имеется статический массив. Запрос - количество элементов с индексами из [L, R), которые лежат во множестве {x, x+1, ... y-1, y}. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
'''Способ 1.''' &amp;lt;br&amp;gt;&lt;br /&gt;
Будем в каждой вершине хранить весь соответсвующий массив в отсортированном виде. Кажется, что о памяти лучше не думать, но на самом деле все относительно прилично: каждый элемент находится от корня на расстоянии O(log n) вершин, поэтому всего дополнительно затрачено будет O(n log n). После этого необходимо найти наибольший элемент, меньший x и наименьший, больший y в массиве нужной вершины. Тогда нужно просто вернуть количество элементов между этими только что найденными. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
'''Способ 2.''' &amp;lt;br&amp;gt;&lt;br /&gt;
Отсортирую элементы по возрастанию (разумеется, с сохранением первоначальной позиции), получив массив S. Создам пустой массив того же размера E, сделаю на нем дерево отрезков. После этого буду пробегать по массиву S и ставить в массив E единицы в соответствии и сохраненным ранее порядком. После этого мне достаточно сделать count (L, R, &amp;quot;&amp;lt;=y&amp;quot;) - count (L, R, &amp;quot;&amp;lt;x&amp;quot;), используя описанную выше систему контроля версий. Памяти мы тратим по-прежнему O(n log n), поскольку у нас n разных ветвей по log(n) каждая.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''Замечание: можно сделать дерево отрезков из деревьев отрезков, чтобы эффективно отвечать на похожие запросы в случае плоскости. ''&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Vis_tree.png&amp;diff=416</id>
		<title>Файл:Vis tree.png</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Vis_tree.png&amp;diff=416"/>
				<updated>2020-05-15T09:45:03Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: Algocourselecturenotes загрузил новую версию Файл:Vis tree.png&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Так выглядит дерево отрезков для задачи RMQ&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Vis_tree.png&amp;diff=415</id>
		<title>Файл:Vis tree.png</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Vis_tree.png&amp;diff=415"/>
				<updated>2020-05-15T09:39:08Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: Так выглядит дерево отрезков для задачи RMQ&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Так выглядит дерево отрезков для задачи RMQ&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=%D0%94%D0%B5%D1%80%D0%B5%D0%B2%D0%BE_%D0%BE%D1%82%D1%80%D0%B5%D0%B7%D0%BA%D0%BE%D0%B2&amp;diff=414</id>
		<title>Дерево отрезков</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=%D0%94%D0%B5%D1%80%D0%B5%D0%B2%D0%BE_%D0%BE%D1%82%D1%80%D0%B5%D0%B7%D0%BA%D0%BE%D0%B2&amp;diff=414"/>
				<updated>2020-05-15T09:38:35Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: /* Структура */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Общая идея ==&lt;br /&gt;
Очень похоже на sparse table - хотим предподсчитывать какие-то значения для отрезков, и обновлять их при изменении дерева.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
''Замечание: рассматриваем на примере задачи RMQ из конспекта про sparse table''&lt;br /&gt;
&lt;br /&gt;
== Структура ==&lt;br /&gt;
На нижнем уровне располагаются элементы массива, на уровень выше - узлы, в которых минимум из двух элементов, на уровень выше - минимум из соответствующих соседей предыдущего уровня и так далее. Рисунок для случая, когда количество элементов в массиве - степень двойки. &lt;br /&gt;
[[Файл:vis_tree.png]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Разумеется, уровней всего O(log n). На i-м уровне, начиная от самого нижнего, n / 2^i элементов. Таким образом, все дерево можно построить (просто сумма членов геометрической прогрессии) за O(n). &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Обработка запроса ==&lt;br /&gt;
Общая идея: процедура getMin начинает от корня дерева и рекурсивно спускается в те отрезки, которые ее интересуют. Пусть мы находимся в вершине x, которая соответствует полуинтервалу TreeSeg = [L_tree, R_tree), а нам нужен минимум на полуинтервале QuerySeg = [L_q, R_q). Строго говоря, возможны три случая:&amp;lt;br&amp;gt;&lt;br /&gt;
1. TreeSeg &amp;lt; QuerySeg. Тогда необходимо вернуть значение из вершины. &amp;lt;br&amp;gt;&lt;br /&gt;
2. TreeSeg () QuerySeg = (/). Необходимо вернуть какую-то большую константу IntMax, или какой-то флаг, показывающий, что отрезки не пересекаются - чтобы уже &amp;quot;вышестоящий орган&amp;quot; думал над тем, что с этим знанием делать. &amp;lt;br&amp;gt;&lt;br /&gt;
3. TreeSeg () QuerySe != (/). Тогда, разумеется, для обработки запроса необходимо вернуть min (getMin (left_half (x)), getMin (right_half (x))). Заметим, что в силу того что left_half и right_half не пересекаются, мы в будущем сможем решать более сложные задачи с помощью этой же структуры.&lt;br /&gt;
&lt;br /&gt;
{{Template:Lemma&lt;br /&gt;
|What = Лемма 1&lt;br /&gt;
|Statement = Каждый такой запрос выполняется за O(log n). &lt;br /&gt;
|Proof = Понятно, что оценка асимптотики алгоритма сводится к подсчету количества рекурсий, которые наша процедура потребует для выполнения. Рассмотрим произвольный уровень нашего дерева при запросе. На нем мы сделаем не более двух рекурсий вниз по дереву (случай 3). Таким образом, количество рекурсий ограничено O(1) * O(height) = O(log n). &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Изменение дерева ==&lt;br /&gt;
''Замечание: на данном этапе предлагается перейти к произвольной функции f элементов на отрезке. Разумеется, нам подойдут не все функции. Необходимо, чтобы эта функция была ассоциативной, поскольку иначе мы не сможет &amp;quot;расставлять скобки&amp;quot; при сведении этой задачи к меньшим. ''&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
''Оговорка: мы храним наше дерево в виде массива - по аналогии с бинарной кучей. ''&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Итак, обрабатываем запрос вида change (i, new_value). Существуют два подхода:&amp;lt;br&amp;gt;&lt;br /&gt;
1. &amp;quot;Изменение снизу&amp;quot;. &amp;lt;br&amp;gt;&lt;br /&gt;
Зная номер вершины i мы можем пересчитать все значения дерева, которые идут на пути от корня до этой вершины (напомним, что индекс родителя при нашей оговорке мы найдем как i / 2). Эта схема плоха тем, что работает только для случая, когда количество элементов - степень двойки. &amp;lt;br&amp;gt;&lt;br /&gt;
2. &amp;quot;Изменение сверху&amp;quot;. &amp;lt;br&amp;gt;&lt;br /&gt;
Аналогично процедуре обработки запроса, мы рекурсивно спускаемся в нужное нам поддерево, переподсчитываем там нашу функцию, после этого обновляем значение в текущей вершины и возвращаем его. &lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Изменение элементов на подотрезке ==&lt;br /&gt;
Пусть нам поступает запрос на присвоение всем элементам с индексами от L до R значения new_value. Вновь применим идею отложенной операции. Будем рекурсивно спускаться от корня пока весь соответствующий текущей вершине полуинтервал не будет лежать в [L, R); когда это произошло, сохраним в этой вершине запрос на изменение. &amp;lt;br&amp;gt;&lt;br /&gt;
Тогда нужно понять, как это повлияет на процедуру  обработки запроса. Разумеется, изменится случай 1 - сейчас нам придется учитывать не только значение вершины, но и эту отложенную операцию, что, впрочем, несильно усложняет дело. Также необходимо в случае 3 &amp;quot;протолкнуть&amp;quot; отложенную операцию в сыновей. Понятно, что никакую асимптотику это нарушить не может. Но здесь важно не забыть после рассмотрения случаев 1, 2, 3 обновить значение в текущей вершине на основе обновленной информации о детях и той ленивой метке, которая сейчас есть. И только после этого можно безопасно возвращаться на уровень выше.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Персистентное дерево отрезков ==&lt;br /&gt;
''Замечание: здесь нам придется отказаться от идеи хранить структуру в виде массива, по аналогии с бинарной кучей.''&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Вдобавок к уже известным функциям и требованием иногда хочется уметь возвращаться к старой версии дерева. Таким образом, мы хотим добавить функцию checkout (version_number), где version_number - количество вызовов функции изменения дерева на подотрезке.&amp;lt;br&amp;gt;&lt;br /&gt;
Заметим, что при каждом вызове функции изменения дерева на подотрезке изменяется лишь O(log n) вершин. (vis 56:46). Более того, при каждой такой операции изменяется лишь один путь от  корня до листа. Итак, мы может создавать новую версию этого пути, возвращая из функции change указатель на новую версию того самого пути. Тогда в каждой вершине на этом пути один из детей будет обновленным, а другой старым.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Имея на руках систему контроля версий, мы можем считать нашу функцию f от полуинтервала исходного массива не только в актуальной версии дерева, но и в любой его версии, имея массив, в котором индекс - номер версии, а значение - соответствующая подветвь.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пример задачи ==&lt;br /&gt;
Имеется статический массив. Запрос - количество элементов с индексами из [L, R), которые лежат во множестве {x, x+1, ... y-1, y}. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
'''Способ 1.''' &amp;lt;br&amp;gt;&lt;br /&gt;
Будем в каждой вершине хранить весь соответсвующий массив в отсортированном виде. Кажется, что о памяти лучше не думать, но на самом деле все относительно прилично: каждый элемент находится от корня на расстоянии O(log n) вершин, поэтому всего дополнительно затрачено будет O(n log n). После этого необходимо найти наибольший элемент, меньший x и наименьший, больший y в массиве нужной вершины. Тогда нужно просто вернуть количество элементов между этими только что найденными. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
'''Способ 2.''' &amp;lt;br&amp;gt;&lt;br /&gt;
Отсортирую элементы по возрастанию (разумеется, с сохранением первоначальной позиции), получив массив S. Создам пустой массив того же размера E, сделаю на нем дерево отрезков. После этого буду пробегать по массиву S и ставить в массив E единицы в соответствии и сохраненным ранее порядком. После этого мне достаточно сделать count (L, R, &amp;quot;&amp;lt;=y&amp;quot;) - count (L, R, &amp;quot;&amp;lt;x&amp;quot;), используя описанную выше систему контроля версий. Памяти мы тратим по-прежнему O(n log n), поскольку у нас n разных ветвей по log(n) каждая.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''Замечание: можно сделать дерево отрезков из деревьев отрезков, чтобы эффективно отвечать на похожие запросы в случае плоскости. ''&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=Sparse_table&amp;diff=413</id>
		<title>Sparse table</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=Sparse_table&amp;diff=413"/>
				<updated>2020-05-15T09:34:43Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: /* Как нам это поможет решить заявленную задачу? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Задача RMQ. ==&lt;br /&gt;
&lt;br /&gt;
'''''Формулировка проблемы.'''''&amp;lt;br&amp;gt;&lt;br /&gt;
Есть массив из n элементов, к нему поступают запросы вида &amp;quot;найти минимум из элементов этого массива с индексами из полунтервала [L, R)&amp;quot;. &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Идея решения == &lt;br /&gt;
Будем предподсчитывать минимумы на полуинтервалах вида [i, i + 2^k). Разумеется, i ∈ [0, n), k ∈ [0, log n]. Таким образом, у нас O(n log n) чисел. &amp;lt;br&amp;gt;&lt;br /&gt;
''Шаг 0.'' &amp;lt;br&amp;gt;&lt;br /&gt;
При k = 0 все, что нужно сделать, это пройтись один раз по массиву, сохранив его, поскольку это просто полуинтервалы вида [i, i + 1), то есть ровно наш изначально заданный массив. &amp;lt;br&amp;gt;&lt;br /&gt;
...&amp;lt;br&amp;gt;&lt;br /&gt;
''Шаг k.'' &amp;lt;br&amp;gt;&lt;br /&gt;
Заметим, что [i, i + 2^k) = [i, i + 2^(k-1)) ∪ [i + 2^(k-1), i + 2^(k-1)). Минимумы на этих полуинтервалах мы считали на предыдущем шаге (иллюстрация ниже). Таким образом, каждое такое значение мы посчитаем за O(1).&amp;lt;br&amp;gt;&lt;br /&gt;
В сухом остатке, мы провели O(log n) шагов ценой O(n) каждый - оценка O(n log n) обоснована.&lt;br /&gt;
[[Файл:vis1.png]]&lt;br /&gt;
&lt;br /&gt;
== Как нам это поможет решить заявленную задачу? ==&lt;br /&gt;
Рассмотрим вновь поступивший запрос GetMin (L, R). Вместим в него слева и справа по полуинтервалу максимальной длины из тех, на которых мы считали минимумы на предыдущем шаге (разумеется, их длины окажутся равны). Докажем, что при таком покрытии покрывающие полуинтервалы обязательно пересекутся. Действительно, пусть это не так. Но тогда каждый из полуинтервалов занимает меньше половины [L, R]. Тогда их можно увеличить в два раза так, что они по прежнему будут внутри [L, R]. Таким образом, получено противоречие с выбором максимальности вписанных полуинтервалов. &amp;lt;br&amp;gt;Но раз эти отрезки пересекаются и мы знаем минимум на каждом из них, предпочитав его, мы можем ответить на запрос за O(1) при условии быстрого нахождения нужных полуинтервалов для вписывания. &amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:vis2.png]]&amp;lt;br&amp;gt;&lt;br /&gt;
Мы можем предподсчитать нужную степень двойки для каждой длины отрезка из запроса. Для этого воспользуемся тем, что для R - L = 1 нам нужна нулевая степень (a[1] = 0), для R - L = 2 - первая (a[2] = 1), ... а для R - L = n соответствующая степень a[n] = a[n/2] + 1. Весь этот цикл выполняется за O(n) - это мелочи по сравнению с тем, сколько мы потратили на предподсчеты минимумов. &amp;lt;br&amp;gt;&lt;br /&gt;
Таким образом, оценка доказана. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
'''''Проблема'''''&amp;lt;br&amp;gt;&lt;br /&gt;
С помощью sparse table мы не можем отвечать на такие запросы в случае, когда массив меняется, поскольку не придуман эффективный по времени алгоритм обновления минимумов на подотрезках.&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Vis2.png&amp;diff=412</id>
		<title>Файл:Vis2.png</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Vis2.png&amp;diff=412"/>
				<updated>2020-05-15T09:33:10Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: Запрос минимума на подотрезке&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Запрос минимума на подотрезке&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=Sparse_table&amp;diff=411</id>
		<title>Sparse table</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=Sparse_table&amp;diff=411"/>
				<updated>2020-05-15T09:32:42Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: /* Как нам это поможет решить заявленную задачу? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Задача RMQ. ==&lt;br /&gt;
&lt;br /&gt;
'''''Формулировка проблемы.'''''&amp;lt;br&amp;gt;&lt;br /&gt;
Есть массив из n элементов, к нему поступают запросы вида &amp;quot;найти минимум из элементов этого массива с индексами из полунтервала [L, R)&amp;quot;. &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Идея решения == &lt;br /&gt;
Будем предподсчитывать минимумы на полуинтервалах вида [i, i + 2^k). Разумеется, i ∈ [0, n), k ∈ [0, log n]. Таким образом, у нас O(n log n) чисел. &amp;lt;br&amp;gt;&lt;br /&gt;
''Шаг 0.'' &amp;lt;br&amp;gt;&lt;br /&gt;
При k = 0 все, что нужно сделать, это пройтись один раз по массиву, сохранив его, поскольку это просто полуинтервалы вида [i, i + 1), то есть ровно наш изначально заданный массив. &amp;lt;br&amp;gt;&lt;br /&gt;
...&amp;lt;br&amp;gt;&lt;br /&gt;
''Шаг k.'' &amp;lt;br&amp;gt;&lt;br /&gt;
Заметим, что [i, i + 2^k) = [i, i + 2^(k-1)) ∪ [i + 2^(k-1), i + 2^(k-1)). Минимумы на этих полуинтервалах мы считали на предыдущем шаге (иллюстрация ниже). Таким образом, каждое такое значение мы посчитаем за O(1).&amp;lt;br&amp;gt;&lt;br /&gt;
В сухом остатке, мы провели O(log n) шагов ценой O(n) каждый - оценка O(n log n) обоснована.&lt;br /&gt;
[[Файл:vis1.png]]&lt;br /&gt;
&lt;br /&gt;
== Как нам это поможет решить заявленную задачу? ==&lt;br /&gt;
Рассмотрим вновь поступивший запрос GetMin (L, R). Вместим в него слева и справа по полуинтервалу максимальной длины из тех, на которых мы считали минимумы на предыдущем шаге (разумеется, их длины окажутся равны). Докажем, что при таком покрытии покрывающие полуинтервалы обязательно пересекутся. Действительно, пусть это не так. Но тогда каждый из полуинтервалов занимает меньше половины [L, R]. Тогда их можно увеличить в два раза так, что они по прежнему будут внутри [L, R]. Таким образом, получено противоречие с выбором максимальности вписанных полуинтервалов. &amp;lt;br&amp;gt;Но раз эти отрезки пересекаются и мы знаем минимум на каждом из них, предпочитав его, мы можем ответить на запрос за O(1) при условии быстрого нахождения нужных полуинтервалов для вписывания. &amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:vis2.png]]&lt;br /&gt;
Мы можем предподсчитать нужную степень двойки для каждой длины отрезка из запроса. Для этого воспользуемся тем, что для R - L = 1 нам нужна нулевая степень (a[1] = 0), для R - L = 2 - первая (a[2] = 1), ... а для R - L = n соответствующая степень a[n] = a[n/2] + 1. Весь этот цикл выполняется за O(n) - это мелочи по сравнению с тем, сколько мы потратили на предподсчеты минимумов. &amp;lt;br&amp;gt;&lt;br /&gt;
Таким образом, оценка доказана. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
'''''Проблема'''''&amp;lt;br&amp;gt;&lt;br /&gt;
С помощью sparse table мы не можем отвечать на такие запросы в случае, когда массив меняется, поскольку не придуман эффективный по времени алгоритм обновления минимумов на подотрезках.&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=Sparse_table&amp;diff=410</id>
		<title>Sparse table</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=Sparse_table&amp;diff=410"/>
				<updated>2020-05-15T09:23:41Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: /* Идея решения */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Задача RMQ. ==&lt;br /&gt;
&lt;br /&gt;
'''''Формулировка проблемы.'''''&amp;lt;br&amp;gt;&lt;br /&gt;
Есть массив из n элементов, к нему поступают запросы вида &amp;quot;найти минимум из элементов этого массива с индексами из полунтервала [L, R)&amp;quot;. &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Идея решения == &lt;br /&gt;
Будем предподсчитывать минимумы на полуинтервалах вида [i, i + 2^k). Разумеется, i ∈ [0, n), k ∈ [0, log n]. Таким образом, у нас O(n log n) чисел. &amp;lt;br&amp;gt;&lt;br /&gt;
''Шаг 0.'' &amp;lt;br&amp;gt;&lt;br /&gt;
При k = 0 все, что нужно сделать, это пройтись один раз по массиву, сохранив его, поскольку это просто полуинтервалы вида [i, i + 1), то есть ровно наш изначально заданный массив. &amp;lt;br&amp;gt;&lt;br /&gt;
...&amp;lt;br&amp;gt;&lt;br /&gt;
''Шаг k.'' &amp;lt;br&amp;gt;&lt;br /&gt;
Заметим, что [i, i + 2^k) = [i, i + 2^(k-1)) ∪ [i + 2^(k-1), i + 2^(k-1)). Минимумы на этих полуинтервалах мы считали на предыдущем шаге (иллюстрация ниже). Таким образом, каждое такое значение мы посчитаем за O(1).&amp;lt;br&amp;gt;&lt;br /&gt;
В сухом остатке, мы провели O(log n) шагов ценой O(n) каждый - оценка O(n log n) обоснована.&lt;br /&gt;
[[Файл:vis1.png]]&lt;br /&gt;
&lt;br /&gt;
== Как нам это поможет решить заявленную задачу? ==&lt;br /&gt;
Рассмотрим вновь поступивший запрос GetMin (L, R). Вместим в него слева и справа по полуинтервалу максимальной длины из тех, на которых мы считали минимумы на предыдущем шаге (разумеется, их длины окажутся равны) (viz 10:20). Докажем, что при таком покрытии покрывающие полуинтервалы обязательно пересекутся. Действительно, пусть это не так. Но тогда каждый из полуинтервалов занимает меньше половины [L, R]. Тогда их можно увеличить в два раза так, что они по прежнему будут внутри [L, R]. Таким образом, получено противоречие с выбором максимальности вписанных полуинтервалов. &amp;lt;br&amp;gt;Но раз эти отрезки пересекаются и мы знаем минимум на каждом из них, предпочитав его, мы можем ответить на запрос за O(1) при условии быстрого нахождения нужных полуинтервалов для вписывания. &amp;lt;br&amp;gt;&lt;br /&gt;
Мы можем предподсчитать нужную степень двойки для каждой длины отрезка из запроса. Для этого воспользуемся тем, что для R - L = 1 нам нужна нулевая степень (a[1] = 0), для R - L = 2 - первая (a[2] = 1), ... а для R - L = n соответствующая степень a[n] = a[n/2] + 1. Весь этот цикл выполняется за O(n) - это мелочи по сравнению с тем, сколько мы потратили на предподсчеты минимумов. &amp;lt;br&amp;gt;&lt;br /&gt;
Таким образом, оценка доказана. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
'''''Проблема'''''&amp;lt;br&amp;gt;&lt;br /&gt;
С помощью sparse table мы не можем отвечать на такие запросы в случае, когда массив меняется, поскольку не придуман эффективный по времени алгоритм обновления минимумов на подотрезках.&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Vis1.png&amp;diff=409</id>
		<title>Файл:Vis1.png</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Vis1.png&amp;diff=409"/>
				<updated>2020-05-15T09:22:15Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: Описание процесса заполнения sparse table&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Описание процесса заполнения sparse table&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=Sparse_table&amp;diff=408</id>
		<title>Sparse table</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=Sparse_table&amp;diff=408"/>
				<updated>2020-05-15T09:21:35Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: /* Идея решения */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Задача RMQ. ==&lt;br /&gt;
&lt;br /&gt;
'''''Формулировка проблемы.'''''&amp;lt;br&amp;gt;&lt;br /&gt;
Есть массив из n элементов, к нему поступают запросы вида &amp;quot;найти минимум из элементов этого массива с индексами из полунтервала [L, R)&amp;quot;. &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Идея решения == &lt;br /&gt;
Будем предподсчитывать минимумы на полуинтервалах вида [i, i + 2^k). Разумеется, i ∈ [0, n), k ∈ [0, log n]. Таким образом, у нас O(n log n) чисел. &amp;lt;br&amp;gt;&lt;br /&gt;
''Шаг 0.'' &amp;lt;br&amp;gt;&lt;br /&gt;
При k = 0 все, что нужно сделать, это пройтись один раз по массиву, сохранив его, поскольку это просто полуинтервалы вида [i, i + 1), то есть ровно наш изначально заданный массив. &amp;lt;br&amp;gt;&lt;br /&gt;
...&amp;lt;br&amp;gt;&lt;br /&gt;
''Шаг k.'' &amp;lt;br&amp;gt;&lt;br /&gt;
Заметим, что [i, i + 2^k) = [i, i + 2^(k-1)) ∪ [i + 2^(k-1), i + 2^(k-1)). Минимумы на этих полуинтервалах мы считали на предыдущем шаге. (vis1). Таким образом, каждое такое значение мы посчитаем за O(1).&amp;lt;br&amp;gt;&lt;br /&gt;
В сухом остатке, мы провели O(log n) шагов ценой O(n) каждый - оценка O(n log n) обоснована.&lt;br /&gt;
[[Файл:vis1.png]]&lt;br /&gt;
&lt;br /&gt;
== Как нам это поможет решить заявленную задачу? ==&lt;br /&gt;
Рассмотрим вновь поступивший запрос GetMin (L, R). Вместим в него слева и справа по полуинтервалу максимальной длины из тех, на которых мы считали минимумы на предыдущем шаге (разумеется, их длины окажутся равны) (viz 10:20). Докажем, что при таком покрытии покрывающие полуинтервалы обязательно пересекутся. Действительно, пусть это не так. Но тогда каждый из полуинтервалов занимает меньше половины [L, R]. Тогда их можно увеличить в два раза так, что они по прежнему будут внутри [L, R]. Таким образом, получено противоречие с выбором максимальности вписанных полуинтервалов. &amp;lt;br&amp;gt;Но раз эти отрезки пересекаются и мы знаем минимум на каждом из них, предпочитав его, мы можем ответить на запрос за O(1) при условии быстрого нахождения нужных полуинтервалов для вписывания. &amp;lt;br&amp;gt;&lt;br /&gt;
Мы можем предподсчитать нужную степень двойки для каждой длины отрезка из запроса. Для этого воспользуемся тем, что для R - L = 1 нам нужна нулевая степень (a[1] = 0), для R - L = 2 - первая (a[2] = 1), ... а для R - L = n соответствующая степень a[n] = a[n/2] + 1. Весь этот цикл выполняется за O(n) - это мелочи по сравнению с тем, сколько мы потратили на предподсчеты минимумов. &amp;lt;br&amp;gt;&lt;br /&gt;
Таким образом, оценка доказана. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
'''''Проблема'''''&amp;lt;br&amp;gt;&lt;br /&gt;
С помощью sparse table мы не можем отвечать на такие запросы в случае, когда массив меняется, поскольку не придуман эффективный по времени алгоритм обновления минимумов на подотрезках.&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=Sparse_table&amp;diff=407</id>
		<title>Sparse table</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=Sparse_table&amp;diff=407"/>
				<updated>2020-05-15T09:18:11Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: /* Идея решения */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Задача RMQ. ==&lt;br /&gt;
&lt;br /&gt;
'''''Формулировка проблемы.'''''&amp;lt;br&amp;gt;&lt;br /&gt;
Есть массив из n элементов, к нему поступают запросы вида &amp;quot;найти минимум из элементов этого массива с индексами из полунтервала [L, R)&amp;quot;. &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Идея решения == &lt;br /&gt;
Будем предподсчитывать минимумы на полуинтервалах вида [i, i + 2^k). Разумеется, i ∈ [0, n), k ∈ [0, log n]. Таким образом, у нас O(n log n) чисел. &amp;lt;br&amp;gt;&lt;br /&gt;
''Шаг 0.'' &amp;lt;br&amp;gt;&lt;br /&gt;
При k = 0 все, что нужно сделать, это пройтись один раз по массиву, сохранив его, поскольку это просто полуинтервалы вида [i, i + 1), то есть ровно наш изначально заданный массив. &amp;lt;br&amp;gt;&lt;br /&gt;
...&amp;lt;br&amp;gt;&lt;br /&gt;
''Шаг k.'' &amp;lt;br&amp;gt;&lt;br /&gt;
Заметим, что [i, i + 2^k) = [i, i + 2^(k-1)) ∪ [i + 2^(k-1), i + 2^(k-1)). Минимумы на этих полуинтервалах мы считали на предыдущем шаге. (vis 7:45). Таким образом, каждое такое значение мы посчитаем за O(1).&amp;lt;br&amp;gt;&lt;br /&gt;
В сухом остатке, мы провели O(log n) шагов ценой O(n) каждый - оценка O(n log n) обоснована.&lt;br /&gt;
[[Файл:example.jpg]]&lt;br /&gt;
&lt;br /&gt;
== Как нам это поможет решить заявленную задачу? ==&lt;br /&gt;
Рассмотрим вновь поступивший запрос GetMin (L, R). Вместим в него слева и справа по полуинтервалу максимальной длины из тех, на которых мы считали минимумы на предыдущем шаге (разумеется, их длины окажутся равны) (viz 10:20). Докажем, что при таком покрытии покрывающие полуинтервалы обязательно пересекутся. Действительно, пусть это не так. Но тогда каждый из полуинтервалов занимает меньше половины [L, R]. Тогда их можно увеличить в два раза так, что они по прежнему будут внутри [L, R]. Таким образом, получено противоречие с выбором максимальности вписанных полуинтервалов. &amp;lt;br&amp;gt;Но раз эти отрезки пересекаются и мы знаем минимум на каждом из них, предпочитав его, мы можем ответить на запрос за O(1) при условии быстрого нахождения нужных полуинтервалов для вписывания. &amp;lt;br&amp;gt;&lt;br /&gt;
Мы можем предподсчитать нужную степень двойки для каждой длины отрезка из запроса. Для этого воспользуемся тем, что для R - L = 1 нам нужна нулевая степень (a[1] = 0), для R - L = 2 - первая (a[2] = 1), ... а для R - L = n соответствующая степень a[n] = a[n/2] + 1. Весь этот цикл выполняется за O(n) - это мелочи по сравнению с тем, сколько мы потратили на предподсчеты минимумов. &amp;lt;br&amp;gt;&lt;br /&gt;
Таким образом, оценка доказана. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
'''''Проблема'''''&amp;lt;br&amp;gt;&lt;br /&gt;
С помощью sparse table мы не можем отвечать на такие запросы в случае, когда массив меняется, поскольку не придуман эффективный по времени алгоритм обновления минимумов на подотрезках.&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=Sparse_table&amp;diff=406</id>
		<title>Sparse table</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=Sparse_table&amp;diff=406"/>
				<updated>2020-05-15T09:06:35Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: /* Идея решения */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Задача RMQ. ==&lt;br /&gt;
&lt;br /&gt;
'''''Формулировка проблемы.'''''&amp;lt;br&amp;gt;&lt;br /&gt;
Есть массив из n элементов, к нему поступают запросы вида &amp;quot;найти минимум из элементов этого массива с индексами из полунтервала [L, R)&amp;quot;. &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Идея решения == &lt;br /&gt;
Будем предподсчитывать минимумы на полуинтервалах вида [i, i + 2^k). Разумеется, i ∈ [0, n), k ∈ [0, log n]. Таким образом, у нас O(n log n) чисел. &amp;lt;br&amp;gt;&lt;br /&gt;
''Шаг 0.'' &amp;lt;br&amp;gt;&lt;br /&gt;
При k = 0 все, что нужно сделать, это пройтись один раз по массиву, сохранив его, поскольку это просто полуинтервалы вида [i, i + 1), то есть ровно наш изначально заданный массив. &amp;lt;br&amp;gt;&lt;br /&gt;
...&amp;lt;br&amp;gt;&lt;br /&gt;
''Шаг k.'' &amp;lt;br&amp;gt;&lt;br /&gt;
Заметим, что [i, i + 2^k) = [i, i + 2^(k-1)) ∪ [i + 2^(k-1), i + 2^(k-1)). Минимумы на этих полуинтервалах мы считали на предыдущем шаге. (vis 7:45). Таким образом, каждое такое значение мы посчитаем за O(1).&amp;lt;br&amp;gt;&lt;br /&gt;
В сухом остатке, мы провели O(log n) шагов ценой O(n) каждый - оценка O(n log n) обоснована.&lt;br /&gt;
&lt;br /&gt;
== Как нам это поможет решить заявленную задачу? ==&lt;br /&gt;
Рассмотрим вновь поступивший запрос GetMin (L, R). Вместим в него слева и справа по полуинтервалу максимальной длины из тех, на которых мы считали минимумы на предыдущем шаге (разумеется, их длины окажутся равны) (viz 10:20). Докажем, что при таком покрытии покрывающие полуинтервалы обязательно пересекутся. Действительно, пусть это не так. Но тогда каждый из полуинтервалов занимает меньше половины [L, R]. Тогда их можно увеличить в два раза так, что они по прежнему будут внутри [L, R]. Таким образом, получено противоречие с выбором максимальности вписанных полуинтервалов. &amp;lt;br&amp;gt;Но раз эти отрезки пересекаются и мы знаем минимум на каждом из них, предпочитав его, мы можем ответить на запрос за O(1) при условии быстрого нахождения нужных полуинтервалов для вписывания. &amp;lt;br&amp;gt;&lt;br /&gt;
Мы можем предподсчитать нужную степень двойки для каждой длины отрезка из запроса. Для этого воспользуемся тем, что для R - L = 1 нам нужна нулевая степень (a[1] = 0), для R - L = 2 - первая (a[2] = 1), ... а для R - L = n соответствующая степень a[n] = a[n/2] + 1. Весь этот цикл выполняется за O(n) - это мелочи по сравнению с тем, сколько мы потратили на предподсчеты минимумов. &amp;lt;br&amp;gt;&lt;br /&gt;
Таким образом, оценка доказана. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
'''''Проблема'''''&amp;lt;br&amp;gt;&lt;br /&gt;
С помощью sparse table мы не можем отвечать на такие запросы в случае, когда массив меняется, поскольку не придуман эффективный по времени алгоритм обновления минимумов на подотрезках.&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=BFS&amp;diff=405</id>
		<title>BFS</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=BFS&amp;diff=405"/>
				<updated>2020-05-14T23:34:41Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: /* Алгоритм */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Breadth-first search''' (сокр. '''BFS''', рус. ''Поиск в ширину'', ''Обход в ширину'') - один из алгоритмов обхода графа. Является довольно простым и эффективным. Часто используется для поиска кратчайшего пути в графе.&lt;br /&gt;
&lt;br /&gt;
==Задача==&lt;br /&gt;
&lt;br /&gt;
Пусть задан неориентированный граф ''G = (V, E)''. Пусть все ребра имеют одинаковый вес, равный, например, ''1''. Пусть также задана некоторая начальная вершина ''S''. Пусть ''расстояние между вершинами'' - сумма весов рёбер, по которым нужно пройти, чтобы попасть из одной вершины в другую. И пусть, наконец, нужно найти кратчайшее расстояние от начальной вершины до всех остальных. Решим задачу, используя '''BFS'''.&lt;br /&gt;
&lt;br /&gt;
==Алгоритм==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Лемма I===&lt;br /&gt;
Кратчайшие пути в графе образуют дерево.&lt;br /&gt;
&lt;br /&gt;
''Доказательство'':&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=BFS&amp;diff=404</id>
		<title>BFS</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=BFS&amp;diff=404"/>
				<updated>2020-05-14T23:34:08Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: /* Лемма I */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Breadth-first search''' (сокр. '''BFS''', рус. ''Поиск в ширину'', ''Обход в ширину'') - один из алгоритмов обхода графа. Является довольно простым и эффективным. Часто используется для поиска кратчайшего пути в графе.&lt;br /&gt;
&lt;br /&gt;
==Задача==&lt;br /&gt;
&lt;br /&gt;
Пусть задан неориентированный граф ''G = (V, E)''. Пусть все ребра имеют одинаковый вес, равный, например, ''1''. Пусть также задана некоторая начальная вершина ''S''. Пусть ''расстояние между вершинами'' - сумма весов рёбер, по которым нужно пройти, чтобы попасть из одной вершины в другую. И пусть, наконец, нужно найти кратчайшее расстояние от начальной вершины до всех остальных. Решим задачу, используя '''BFS'''.&lt;br /&gt;
&lt;br /&gt;
==Алгоритм==&lt;br /&gt;
&lt;br /&gt;
===Лемма I===&lt;br /&gt;
Кратчайшие пути в графе образуют дерево.'\n'&lt;br /&gt;
''Доказательство'':&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=BFS&amp;diff=403</id>
		<title>BFS</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=BFS&amp;diff=403"/>
				<updated>2020-05-14T23:33:32Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: /* Лемма I */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Breadth-first search''' (сокр. '''BFS''', рус. ''Поиск в ширину'', ''Обход в ширину'') - один из алгоритмов обхода графа. Является довольно простым и эффективным. Часто используется для поиска кратчайшего пути в графе.&lt;br /&gt;
&lt;br /&gt;
==Задача==&lt;br /&gt;
&lt;br /&gt;
Пусть задан неориентированный граф ''G = (V, E)''. Пусть все ребра имеют одинаковый вес, равный, например, ''1''. Пусть также задана некоторая начальная вершина ''S''. Пусть ''расстояние между вершинами'' - сумма весов рёбер, по которым нужно пройти, чтобы попасть из одной вершины в другую. И пусть, наконец, нужно найти кратчайшее расстояние от начальной вершины до всех остальных. Решим задачу, используя '''BFS'''.&lt;br /&gt;
&lt;br /&gt;
==Алгоритм==&lt;br /&gt;
&lt;br /&gt;
==Лемма I==&lt;br /&gt;
Кратчайшие пути в графе образуют дерево.'\n'&lt;br /&gt;
''Доказательство'':&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=BFS&amp;diff=402</id>
		<title>BFS</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=BFS&amp;diff=402"/>
				<updated>2020-05-14T23:33:06Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: /* Лемма I */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Breadth-first search''' (сокр. '''BFS''', рус. ''Поиск в ширину'', ''Обход в ширину'') - один из алгоритмов обхода графа. Является довольно простым и эффективным. Часто используется для поиска кратчайшего пути в графе.&lt;br /&gt;
&lt;br /&gt;
==Задача==&lt;br /&gt;
&lt;br /&gt;
Пусть задан неориентированный граф ''G = (V, E)''. Пусть все ребра имеют одинаковый вес, равный, например, ''1''. Пусть также задана некоторая начальная вершина ''S''. Пусть ''расстояние между вершинами'' - сумма весов рёбер, по которым нужно пройти, чтобы попасть из одной вершины в другую. И пусть, наконец, нужно найти кратчайшее расстояние от начальной вершины до всех остальных. Решим задачу, используя '''BFS'''.&lt;br /&gt;
&lt;br /&gt;
==Алгоритм==&lt;br /&gt;
&lt;br /&gt;
==Лемма I==&lt;br /&gt;
Кратчайшие пути в графе образуют дерево.&lt;br /&gt;
''Доказательство'':&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=BFS&amp;diff=401</id>
		<title>BFS</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=BFS&amp;diff=401"/>
				<updated>2020-05-14T23:32:50Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: /* Лемма 1 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Breadth-first search''' (сокр. '''BFS''', рус. ''Поиск в ширину'', ''Обход в ширину'') - один из алгоритмов обхода графа. Является довольно простым и эффективным. Часто используется для поиска кратчайшего пути в графе.&lt;br /&gt;
&lt;br /&gt;
==Задача==&lt;br /&gt;
&lt;br /&gt;
Пусть задан неориентированный граф ''G = (V, E)''. Пусть все ребра имеют одинаковый вес, равный, например, ''1''. Пусть также задана некоторая начальная вершина ''S''. Пусть ''расстояние между вершинами'' - сумма весов рёбер, по которым нужно пройти, чтобы попасть из одной вершины в другую. И пусть, наконец, нужно найти кратчайшее расстояние от начальной вершины до всех остальных. Решим задачу, используя '''BFS'''.&lt;br /&gt;
&lt;br /&gt;
==Алгоритм==&lt;br /&gt;
&lt;br /&gt;
==Лемма I==&lt;br /&gt;
Кратчайшие пути в графе образуют дерево.&lt;br /&gt;
''Доказательство'':&lt;br /&gt;
&amp;lt;math&amp;gt; \leq &amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=BFS&amp;diff=400</id>
		<title>BFS</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=BFS&amp;diff=400"/>
				<updated>2020-05-14T23:31:06Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: /* Алгоритм */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Breadth-first search''' (сокр. '''BFS''', рус. ''Поиск в ширину'', ''Обход в ширину'') - один из алгоритмов обхода графа. Является довольно простым и эффективным. Часто используется для поиска кратчайшего пути в графе.&lt;br /&gt;
&lt;br /&gt;
==Задача==&lt;br /&gt;
&lt;br /&gt;
Пусть задан неориентированный граф ''G = (V, E)''. Пусть все ребра имеют одинаковый вес, равный, например, ''1''. Пусть также задана некоторая начальная вершина ''S''. Пусть ''расстояние между вершинами'' - сумма весов рёбер, по которым нужно пройти, чтобы попасть из одной вершины в другую. И пусть, наконец, нужно найти кратчайшее расстояние от начальной вершины до всех остальных. Решим задачу, используя '''BFS'''.&lt;br /&gt;
&lt;br /&gt;
==Алгоритм==&lt;br /&gt;
&lt;br /&gt;
==Лемма 1==&lt;br /&gt;
Кратчайшие пути в графе образуют дерево.&lt;br /&gt;
''Доказательство'':&lt;br /&gt;
&amp;lt;math&amp;gt; V &amp;lt;\math&amp;gt;&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=BFS&amp;diff=399</id>
		<title>BFS</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=BFS&amp;diff=399"/>
				<updated>2020-05-14T23:28:05Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: /* Алгоритм */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Breadth-first search''' (сокр. '''BFS''', рус. ''Поиск в ширину'', ''Обход в ширину'') - один из алгоритмов обхода графа. Является довольно простым и эффективным. Часто используется для поиска кратчайшего пути в графе.&lt;br /&gt;
&lt;br /&gt;
==Задача==&lt;br /&gt;
&lt;br /&gt;
Пусть задан неориентированный граф ''G = (V, E)''. Пусть все ребра имеют одинаковый вес, равный, например, ''1''. Пусть также задана некоторая начальная вершина ''S''. Пусть ''расстояние между вершинами'' - сумма весов рёбер, по которым нужно пройти, чтобы попасть из одной вершины в другую. И пусть, наконец, нужно найти кратчайшее расстояние от начальной вершины до всех остальных. Решим задачу, используя '''BFS'''.&lt;br /&gt;
&lt;br /&gt;
==Алгоритм==&lt;br /&gt;
&lt;br /&gt;
{{Лемма 1&lt;br /&gt;
|statement=&lt;br /&gt;
Кратчайшие пути в графе образуют дерево.&lt;br /&gt;
|proof=&lt;br /&gt;
I sware&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=BFS&amp;diff=398</id>
		<title>BFS</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=BFS&amp;diff=398"/>
				<updated>2020-05-14T23:27:06Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Breadth-first search''' (сокр. '''BFS''', рус. ''Поиск в ширину'', ''Обход в ширину'') - один из алгоритмов обхода графа. Является довольно простым и эффективным. Часто используется для поиска кратчайшего пути в графе.&lt;br /&gt;
&lt;br /&gt;
==Задача==&lt;br /&gt;
&lt;br /&gt;
Пусть задан неориентированный граф ''G = (V, E)''. Пусть все ребра имеют одинаковый вес, равный, например, ''1''. Пусть также задана некоторая начальная вершина ''S''. Пусть ''расстояние между вершинами'' - сумма весов рёбер, по которым нужно пройти, чтобы попасть из одной вершины в другую. И пусть, наконец, нужно найти кратчайшее расстояние от начальной вершины до всех остальных. Решим задачу, используя '''BFS'''.&lt;br /&gt;
&lt;br /&gt;
==Алгоритм==&lt;br /&gt;
&lt;br /&gt;
{{Теорема&lt;br /&gt;
|statement=&lt;br /&gt;
hello world&lt;br /&gt;
|proof=&lt;br /&gt;
I sware&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=BFS&amp;diff=397</id>
		<title>BFS</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=BFS&amp;diff=397"/>
				<updated>2020-05-14T23:11:45Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Breadth-first search''' (сокр. '''BFS''', рус. ''Поиск в ширину'', ''Обход в ширину'') - один из алгоритмов обхода графа.&lt;br /&gt;
&lt;br /&gt;
==Задача==&lt;br /&gt;
&lt;br /&gt;
Пусть задан неориентированный граф ''G = (V, E)''. Пусть все ребра имеют одинаковый вес, например, ''1''. Пусть также задана некоторая начальная вершина ''S'' (\in) \leq&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=BFS&amp;diff=396</id>
		<title>BFS</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=BFS&amp;diff=396"/>
				<updated>2020-05-14T22:58:02Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: Заголовок написан&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Breadth-first search''' (сокр. '''BFS''', рус. ''Поиск в ширину'', ''Обход в ширину'') - один из алгоритмов обхода графа.&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=%D0%94%D0%9F_%D0%BD%D0%B0_%D0%BF%D0%BE%D0%B4%D0%BC%D0%BD%D0%BE%D0%B6%D0%B5%D1%81%D1%82%D0%B2%D0%B0%D1%85&amp;diff=395</id>
		<title>ДП на подмножествах</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=%D0%94%D0%9F_%D0%BD%D0%B0_%D0%BF%D0%BE%D0%B4%D0%BC%D0%BD%D0%BE%D0%B6%D0%B5%D1%81%D1%82%D0%B2%D0%B0%D1%85&amp;diff=395"/>
				<updated>2020-05-14T22:50:09Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: Исправил опечатки&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Проблема представления подмножеств ==&lt;br /&gt;
Пусть у нас есть некоторое множество N = {0, 1, 2, ..., n - 1}, n &amp;amp;le; 30 &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Мы хотим получить ответить на вопрос: &amp;quot; Как эффективно хранить и кодировать подмножества N?&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== Битовые маски ==&lt;br /&gt;
===Что такое битовые маски?===&lt;br /&gt;
Подмножество будем кодировать с помощью двоичного числа, в котором на i-й позиции стоит 1, если i-й элемент множества входит в это подмножество, и 0 в противном случае. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Например, если есть множество А = {3, 5, 7, 9}, то его подмножество B = {3, 7, 9} можно закодировать с помощью маски 1011&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; = 11&amp;lt;sub&amp;gt;10&amp;lt;/sub&amp;gt;. Таким образом, если мы будем кодировать подмножества с помощью десятичного числа типа &amp;lt;code&amp;gt;unsigned int&amp;lt;/code&amp;gt;, то сможем закодировать любое подмножество, размер которого не больше 32х. &lt;br /&gt;
===Как работать с множествами, с помощью масок?===&lt;br /&gt;
&amp;lt;p&amp;gt;Вот так на языке С будет выглядеть функция проверяющая, входит ли элемент множества, стоящий на позиции pos в подмножество с маской mask:&amp;lt;/p&amp;gt;&lt;br /&gt;
    '''bool''' elem_in_subset ('''unsigned int''' mask, '''int''' pos) {&lt;br /&gt;
        return (mask &amp;gt;&amp;gt; pos) &amp;amp; 1;&lt;br /&gt;
    }&lt;br /&gt;
Похожим образом выглядят функции добавления и удаления элемента из подмножества. Для подсчета количества элементов в подмножестве подойдет функция &amp;lt;code&amp;gt;__builtin_popcount (mask)&amp;lt;/code&amp;gt;, которая возвращает количество единичных битов в двоичном представлении mask. Пересечение и объединение подмножеств соответствует побитовому пересечению и побитовому объединению масок, соответствующих данным подмножествам.&lt;br /&gt;
==ДП на подмножествах==&lt;br /&gt;
===Задача о гамильтоновом цикле===&lt;br /&gt;
1) Пусть есть неориентированный взвешенный граф (можно считать, что между любыми двумя вершинами есть ребро). Необходимо найти [https://ru.wikipedia.org/wiki/%D0%93%D0%BB%D0%BE%D1%81%D1%81%D0%B0%D1%80%D0%B8%D0%B9_%D1%82%D0%B5%D0%BE%D1%80%D0%B8%D0%B8_%D0%B3%D1%80%D0%B0%D1%84%D0%BE%D0%B2#%D0%9F гамильтонов цикл] наименьшего веса.&lt;br /&gt;
====Наивное решение====&lt;br /&gt;
Наивное решение заключается в полном переборе всех гамильтонов циклов и выборе из них цикла с наименьшей стоимостью. Количество циклов равно n! (количество перестановок вершин). Решение можно улучшить до O ($\frac {(n-1)!}{2}$), если заметить, что нам не важно из какой вершины начинать обход и в какую сторону обходить.&lt;br /&gt;
====Оптимизация наивного решения с помощью ДП====&lt;br /&gt;
Заведем массив &amp;lt;code&amp;gt;dp&amp;lt;/code&amp;gt; такой, что &amp;lt;code&amp;gt;dp[mask][v]&amp;lt;/code&amp;gt; - минимальная длина пути из 0-й вершины в вершину v, проходящий только по вершинам из множества mask ровно по одному разу.&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D1%8B_%D0%98%D0%92%D0%A2_2020&amp;diff=393</id>
		<title>Алгоритмы ИВТ 2020</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D1%8B_%D0%98%D0%92%D0%A2_2020&amp;diff=393"/>
				<updated>2020-05-11T22:46:07Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Лекции и семинары ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! # !!Лекция !! Конспектирующий !! Семинар &lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| Базовые структуры, амортизационный анализ&lt;br /&gt;
  [[Вектор]]&lt;br /&gt;
  [[Стек]]&lt;br /&gt;
  [[Дек]] (Double-ended queue)&lt;br /&gt;
  [[Очередь]]&lt;br /&gt;
  [[Skip-list]]&lt;br /&gt;
| Андрей Саранчин&lt;br /&gt;
| [https://drive.google.com/open?id=1kSmSdV2jcRscsNXx2FUtwdiWcR4Th5yN Задачи на базовые структуры, амортизационный анализ]&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| Сортировки&lt;br /&gt;
  [[Quicksort]]&lt;br /&gt;
  [[Сортировка слиянием]]&lt;br /&gt;
  [[Цифровая сортировка]]&lt;br /&gt;
  [[Сортировка подсчётом]]&lt;br /&gt;
&lt;br /&gt;
[[QuickSelect]]&lt;br /&gt;
&lt;br /&gt;
| Екатерина Дробченко&lt;br /&gt;
| [https://drive.google.com/file/d/13PsIUrDpRT51qPmHuS5xp-N_Y17Ujq2o/view?usp=sharing Сортировки, бинпоиск, сканлайн ]&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| Кучи&lt;br /&gt;
  [[Бинарная куча]]&lt;br /&gt;
  [[Quickheap]]&lt;br /&gt;
  [[Биномиальная куча]]&lt;br /&gt;
| Максим Коробко&lt;br /&gt;
| [https://drive.google.com/file/d/17QbjgmcMngRKUWOQfmdjSnxL1WtUZXon/view?usp=sharing Задачи на кучи]&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| Деревья поиска-1&lt;br /&gt;
  [[Наивное дерево поиска]]&lt;br /&gt;
  [[АВЛ-дерево]]&lt;br /&gt;
  [[Splay-дерево]]&lt;br /&gt;
| Михаил Макей&lt;br /&gt;
| -&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| Деревья поиска-2&lt;br /&gt;
  [[Красное-чёрное дерево]]&lt;br /&gt;
  [[B-дерево]]&lt;br /&gt;
| Михаил Воробьёв&lt;br /&gt;
| [https://drive.google.com/open?id=1Bac6S7YXgKogKvZQglI23BIRRwg2TIBp Задачи на деревья поиска]&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| Деревья поиска-3&lt;br /&gt;
 [[ Декартово дерево (Treap) ]]&lt;br /&gt;
 [[ Декартово дерево по неявному ключу ]]&lt;br /&gt;
| Сергей Новиков&lt;br /&gt;
| [https://drive.google.com/open?id=1ftyGP8GgiDZ_lr_9XHCkK0HP_0RD1Fed Задачи на декартово дерево]&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| [[Sparse table]], [[Дерево отрезков]]&lt;br /&gt;
| Алексей Кудринский&lt;br /&gt;
| [https://drive.google.com/open?id=1evEyPLk41fB4G4TeBBKZndFlqPin4CbM Задачи]&lt;br /&gt;
|-&lt;br /&gt;
| 8&lt;br /&gt;
| Дерево Фенвика&lt;br /&gt;
  [[Одномерное дерево Фенвика]]&lt;br /&gt;
  [[Двумерное дерево Фенвика]]&lt;br /&gt;
  [[Фенвик Фенвиков]]&lt;br /&gt;
| Эвелина Емельянова&lt;br /&gt;
| [https://drive.google.com/open?id=1GbH5nHWet05SWvo3LtnB_ux8nDUZsJjl Задачи на дерево Фенвика]&lt;br /&gt;
|-&lt;br /&gt;
| 9&lt;br /&gt;
| Динамическое программирование-1&lt;br /&gt;
  [[Простые задачи на динамическое программирование]]&lt;br /&gt;
  [[ДП на подотрезках]]&lt;br /&gt;
  [[Задача НОП]]&lt;br /&gt;
  [[Задача НВП]]&lt;br /&gt;
  [[Задача о рюкзаке]]&lt;br /&gt;
| Данил Милько&lt;br /&gt;
| [https://drive.google.com/open?id=15Vj-wM7TrqV7YYIrgiaEvOJYNCf1bwLO Задачи на ДП-1]&lt;br /&gt;
|-&lt;br /&gt;
| 10&lt;br /&gt;
| Динамическое програмирование-2&lt;br /&gt;
  [[ДП на подмножествах]]&lt;br /&gt;
  [[Подсчёт ДП с помощью матричного умножения]]&lt;br /&gt;
| Анастас Беляев&lt;br /&gt;
| [https://drive.google.com/open?id=1WXJK7pFUzNL-C5yXNB1Yn289olaWDhtN Задачи на ДП-2]&lt;br /&gt;
|-&lt;br /&gt;
| 11&lt;br /&gt;
| Графы-1, обход в глубину&lt;br /&gt;
  [[DFS]]&lt;br /&gt;
  [[Поиск компонент сильной связности]]&lt;br /&gt;
  [[Поиск мостов, точек сочленения и компонент двусвязности]]&lt;br /&gt;
  [[Задача 2-SAT]]&lt;br /&gt;
| Марина Коновалова&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 12&lt;br /&gt;
| Графы-2, Кратчайшие пути-1&lt;br /&gt;
  [[BFS]]&lt;br /&gt;
  [[0-k BFS]]&lt;br /&gt;
  [[Алгоритм Дейкстры]]&lt;br /&gt;
| Константин Драгун&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Домашки ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! # !!Тема !! Контест !! Листок&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| Базовые структуры, скиплисты, бинпоиск &lt;br /&gt;
| [https://codeforces.com/group/iMwjhG4D4C/contest/268257 тык] Обязательные: C, I, J&lt;br /&gt;
| [https://drive.google.com/open?id=1lLRga6zeGilg1SFfPSJZVqbQHsCYbOzj пунь] Обязательные: 2, 3, 9&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| Кучи&lt;br /&gt;
| [https://codeforces.com/group/iMwjhG4D4C/contest/269940 контест мой контест] Обязательная: A&lt;br /&gt;
| [https://drive.google.com/open?id=1Pdn9bC0HEMvnyzC7bKqC6sXD1drYi2b- домашка моя домашка]&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| Деревья поиска&lt;br /&gt;
| [https://codeforces.com/group/iMwjhG4D4C/contest/272242 коронаконтест] Обязательные задачи: A и D. STL разрешается использовать только в задачах E-G.&lt;br /&gt;
| [https://drive.google.com/open?id=1ftyGP8GgiDZ_lr_9XHCkK0HP_0RD1Fed коронадомашка]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Правила игры ==&lt;br /&gt;
&lt;br /&gt;
* Оценка за экзамен ставится следующим образом:&lt;br /&gt;
** Если вас устраивает накопленная оценка за семестр, то она и выставляется в качестве оценки за экзамен.&lt;br /&gt;
** Если вы хотите поднять итоговую оценку, то вы можете прийти на экзамен и получить за него оценку EXAM от 0 до 10, после чего ваша итоговая оценка будет равна ROUND(0.3*EXAM + 0.7*SEMESTER) .&lt;br /&gt;
* Баллы за семестр набираются за контесты и теор.домашки и конспекты лекций. Всего будет 5-7 контестов и 5-7 теор.домашек.&lt;br /&gt;
** Решённые полностью все контесты и задачи дают вам отл (14). Ваша оценка равна MIN(10, FLOOR((STUDENT_SCORE / MAX_SCORE)  * 14)) - PENALTY .&lt;br /&gt;
** В каждом контесте/листочке есть некоторое количество обязательных задач. Чтобы их сдать, надо получить OK в контесте и защитить решение перед семинаристом или ассистентом на очной сдаче.&lt;br /&gt;
** Дедлайн на устную сдачу: 2 недели от дедлайна контеста/листочка.&lt;br /&gt;
** За незащищённую обязательную задачу ваша итоговая оценка понижается на 1 балл.&lt;br /&gt;
* Конспекты лекций ведутся в системе MediaWiki на этом сайте. Если вы хотите получить 10 бонусных баллов к STUDENT_SCORE и уважение от ваших однокурсников за конспектирование лекций, напишите об этом в телеграм Саше Гришутину (@rationalex). Заявляя своё желание, вы подтверждаете, что готовы законспектировать любую из лекций, на которой вы были (это нужно для того, чтобы равномерно распределить всех желающих по лекциям). При наличии нескольких желающих вести конспект, приоритет будет отдаваться тем, кто получил меньший бонусный балл за ведение конспектов. При равенстве всех пунктов, приоритет будет отдаваться тем, кто до этого сделал конспекты прилежнее и быстрее.&lt;br /&gt;
*Если вы в какой-то момент поймёте, что курс вам неинтересен, то вы можете получить джентльменский уд(3), сдав и защитив все обязательные задачи и больше ничего.&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=Quicksort&amp;diff=390</id>
		<title>Quicksort</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=Quicksort&amp;diff=390"/>
				<updated>2020-05-07T14:58:12Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Быстрая сортировка(std sort) ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
p&lt;br /&gt;
	&lt;br /&gt;
p&lt;br /&gt;
p&lt;br /&gt;
&lt;br /&gt;
                 p (pivot)&lt;br /&gt;
&amp;lt;p&amp;lt;p&amp;lt;p&amp;lt;p p &amp;gt;p&amp;gt;p&amp;gt;p&lt;br /&gt;
                                                                                            &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Пусть выбран p. Как построить разбиение?&lt;br /&gt;
Метод 1. (С доп. памятью)&lt;br /&gt;
- исх. массив а&lt;br /&gt;
C - результат&lt;br /&gt;
i&lt;br /&gt;
p&lt;br /&gt;
                                                                              &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 а) for (i = 1…n)&lt;br /&gt;
Стабильная.&lt;br /&gt;
          if (a &amp;lt; i) &amp;lt; p)&lt;br /&gt;
               c.push_back(a(i))&lt;br /&gt;
б) for (i = 1…n)&lt;br /&gt;
           ai = p&lt;br /&gt;
в) for (i = 1…n)&lt;br /&gt;
           ai &amp;gt; p&lt;br /&gt;
pppp&lt;br /&gt;
                                                   &lt;br /&gt;
r&lt;br /&gt;
r&lt;br /&gt;
l&lt;br /&gt;
l&lt;br /&gt;
c                                                      &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
for (I = 1…n)&lt;br /&gt;
Не стабильная.&lt;br /&gt;
      if (a(i) &amp;lt; p): c (l) = a (i), ++ l&lt;br /&gt;
      if (a(i) &amp;gt; p): c (r) = a (i), -- r&lt;br /&gt;
&lt;br /&gt;
Метод 2.&lt;br /&gt;
i = 1, i = n&lt;br /&gt;
while (i &amp;lt; j): &lt;br /&gt;
          while (i &amp;lt; j &amp;amp;&amp;amp; a(i)  p) ++i;&lt;br /&gt;
          while (i &amp;lt; j &amp;amp;&amp;amp; a(j)  p) -- j;&lt;br /&gt;
           if (i &amp;lt; j) &lt;br /&gt;
                swap (a(i), a(j)), ++i, -- j;&lt;br /&gt;
           swap (a(micl), a(j));&lt;br /&gt;
                             нач. позиция p (в исх. массиве)&lt;br /&gt;
i и j находят разделение p и  p&lt;br /&gt;
&lt;br /&gt;
1) Произвольный детерм. выбор ( mid = 1 / micl = n / …)&lt;br /&gt;
     В худшем случае может работать &lt;br /&gt;
&lt;br /&gt;
2) Выбор случайного p.&lt;br /&gt;
Теорема (б/д). В среднем quick sort со случайным выбором p работает за&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=Quicksort&amp;diff=389</id>
		<title>Quicksort</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=Quicksort&amp;diff=389"/>
				<updated>2020-05-07T14:49:34Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: Новая страница: «Быстрая сортировка (std sort)   p 	 p p                   p (pivot) &amp;lt;p&amp;lt;p&amp;lt;p&amp;lt;p p &amp;gt;p&amp;gt;p&amp;gt;p…»&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Быстрая сортировка&lt;br /&gt;
(std sort)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
p&lt;br /&gt;
	&lt;br /&gt;
p&lt;br /&gt;
p&lt;br /&gt;
&lt;br /&gt;
                 p (pivot)&lt;br /&gt;
&amp;lt;p&amp;lt;p&amp;lt;p&amp;lt;p p &amp;gt;p&amp;gt;p&amp;gt;p&lt;br /&gt;
                                                                                            &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Пусть выбран p. Как построить разбиение?&lt;br /&gt;
Метод 1. (С доп. памятью)&lt;br /&gt;
- исх. массив а&lt;br /&gt;
C - результат&lt;br /&gt;
i&lt;br /&gt;
p&lt;br /&gt;
                                                                              &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 а) for (i = 1…n)&lt;br /&gt;
Стабильная.&lt;br /&gt;
          if (a &amp;lt; i) &amp;lt; p)&lt;br /&gt;
               c.push_back(a(i))&lt;br /&gt;
б) for (i = 1…n)&lt;br /&gt;
           ai = p&lt;br /&gt;
в) for (i = 1…n)&lt;br /&gt;
           ai &amp;gt; p&lt;br /&gt;
pppp&lt;br /&gt;
                                                   &lt;br /&gt;
r&lt;br /&gt;
r&lt;br /&gt;
l&lt;br /&gt;
l&lt;br /&gt;
c                                                      &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
for (I = 1…n)&lt;br /&gt;
Не стабильная.&lt;br /&gt;
      if (a(i) &amp;lt; p): c (l) = a (i), ++ l&lt;br /&gt;
      if (a(i) &amp;gt; p): c (r) = a (i), -- r&lt;br /&gt;
&lt;br /&gt;
Метод 2.&lt;br /&gt;
i = 1, i = n&lt;br /&gt;
while (i &amp;lt; j): &lt;br /&gt;
          while (i &amp;lt; j &amp;amp;&amp;amp; a(i)  p) ++i;&lt;br /&gt;
          while (i &amp;lt; j &amp;amp;&amp;amp; a(j)  p) -- j;&lt;br /&gt;
           if (i &amp;lt; j) &lt;br /&gt;
                swap (a(i), a(j)), ++i, -- j;&lt;br /&gt;
           swap (a(micl), a(j));&lt;br /&gt;
                             нач. позиция p (в исх. массиве)&lt;br /&gt;
i и j находят разделение p и  p&lt;br /&gt;
&lt;br /&gt;
1) Произвольный детерм. выбор ( mid = 1 / micl = n / …)&lt;br /&gt;
     В худшем случае может работать &lt;br /&gt;
&lt;br /&gt;
2) Выбор случайного p.&lt;br /&gt;
Теорема (б/д). В среднем quick sort со случайным выбором p работает за&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D1%8B_%D0%98%D0%92%D0%A2_2020&amp;diff=386</id>
		<title>Алгоритмы ИВТ 2020</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D1%8B_%D0%98%D0%92%D0%A2_2020&amp;diff=386"/>
				<updated>2020-05-04T10:28:02Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: /* Лекции и семинары */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Лекции и семинары ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! # !!Лекция !! Конспектирующий !! Семинар &lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| Базовые структуры, амортизационный анализ&lt;br /&gt;
  [[Вектор]]&lt;br /&gt;
  [[Стек]]&lt;br /&gt;
  [[Дек]] (Double-ended queue)&lt;br /&gt;
  [[Очередь]]&lt;br /&gt;
  [[Skip-list]]&lt;br /&gt;
| Андрей Саранчин&lt;br /&gt;
| [https://drive.google.com/open?id=1kSmSdV2jcRscsNXx2FUtwdiWcR4Th5yN Задачи на базовые структуры, амортизационный анализ]&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| Сортировки&lt;br /&gt;
  [[Quicksort]]&lt;br /&gt;
  [[Сортировка слиянием]]&lt;br /&gt;
  [[Цифровая сортировка]]&lt;br /&gt;
  [[Сортировка подсчётом]]&lt;br /&gt;
&lt;br /&gt;
[[QuickSelect]]&lt;br /&gt;
&lt;br /&gt;
| Екатерина Дробченко&lt;br /&gt;
| [https://drive.google.com/file/d/13PsIUrDpRT51qPmHuS5xp-N_Y17Ujq2o/view?usp=sharing Сортировки, бинпоиск, сканлайн ]&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| Кучи&lt;br /&gt;
  [[Бинарная куча]]&lt;br /&gt;
  [[Quickheap]]&lt;br /&gt;
  [[Биномиальная куча]]&lt;br /&gt;
| Максим Коробко&lt;br /&gt;
| [https://drive.google.com/file/d/17QbjgmcMngRKUWOQfmdjSnxL1WtUZXon/view?usp=sharing Задачи на кучи]&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| Деревья поиска-1&lt;br /&gt;
  [[Наивное дерево поиска]]&lt;br /&gt;
  [[АВЛ-дерево]]&lt;br /&gt;
  [[Splay-дерево]]&lt;br /&gt;
| Михаил Макей&lt;br /&gt;
| -&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| Деревья поиска-2&lt;br /&gt;
  [[Красное-чёрное дерево]]&lt;br /&gt;
  [[B-дерево]]&lt;br /&gt;
| Михаил Воробьёв&lt;br /&gt;
| [https://drive.google.com/open?id=1Bac6S7YXgKogKvZQglI23BIRRwg2TIBp Задачи на деревья поиска]&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| Деревья поиска-3&lt;br /&gt;
 [[ Декартово дерево (Treap) ]]&lt;br /&gt;
 [[ Декартово дерево по неявному ключу ]]&lt;br /&gt;
| Сергей Новиков&lt;br /&gt;
| [https://drive.google.com/open?id=1ftyGP8GgiDZ_lr_9XHCkK0HP_0RD1Fed Задачи на декартово дерево]&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| [[Sparse table]], [[Дерево отрезков]]&lt;br /&gt;
| Алексей Кудринский&lt;br /&gt;
| [https://drive.google.com/open?id=1evEyPLk41fB4G4TeBBKZndFlqPin4CbM Задачи]&lt;br /&gt;
|-&lt;br /&gt;
| 8&lt;br /&gt;
| Дерево Фенвика&lt;br /&gt;
  [[Одномерное дерево Фенвика]]&lt;br /&gt;
  [[Двумерное дерево Фенвика]]&lt;br /&gt;
  [[Фенвик Фенвиков]]&lt;br /&gt;
| Эвелина Емельянова&lt;br /&gt;
| [https://drive.google.com/open?id=1GbH5nHWet05SWvo3LtnB_ux8nDUZsJjl Задачи на дерево Фенвика]&lt;br /&gt;
|-&lt;br /&gt;
| 9&lt;br /&gt;
| Динамическое программирование-1&lt;br /&gt;
  [[Простые задачи на динамическое программирование]]&lt;br /&gt;
  [[ДП на подотрезках]]&lt;br /&gt;
  [[Задача НОП]]&lt;br /&gt;
  [[Задача НВП]]&lt;br /&gt;
  [[Задача о рюкзаке]]&lt;br /&gt;
| Данил Милько&lt;br /&gt;
| [https://drive.google.com/open?id=15Vj-wM7TrqV7YYIrgiaEvOJYNCf1bwLO Задачи на ДП-1]&lt;br /&gt;
|-&lt;br /&gt;
| 10&lt;br /&gt;
| Динамическое програмирование-2&lt;br /&gt;
  [[ДП на подмножествах]]&lt;br /&gt;
  [[Подсчёт ДП с помощью матричного умножения]]&lt;br /&gt;
| Анастас Беляев&lt;br /&gt;
| [https://drive.google.com/open?id=1WXJK7pFUzNL-C5yXNB1Yn289olaWDhtN Задачи на ДП-2]&lt;br /&gt;
|-&lt;br /&gt;
| 11&lt;br /&gt;
| Графы-1, обход в глубину&lt;br /&gt;
  [[DFS]]&lt;br /&gt;
  [[Поиск компонент сильной связности]]&lt;br /&gt;
  [[Поиск мостов, точек сочленения и компонент двусвязности]]&lt;br /&gt;
  [[Задача 2-SAT]]&lt;br /&gt;
| WANTED&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| 12&lt;br /&gt;
| Графы-2, Кратчайшие пути-1&lt;br /&gt;
  [[BFS]]&lt;br /&gt;
  [[0-k BFS]]&lt;br /&gt;
  [[Алгоритм Дейкстры]]&lt;br /&gt;
| WANTED&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Домашки ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! # !!Тема !! Контест !! Листок&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| Базовые структуры, скиплисты, бинпоиск &lt;br /&gt;
| [https://codeforces.com/group/iMwjhG4D4C/contest/268257 тык] Обязательные: C, I, J&lt;br /&gt;
| [https://drive.google.com/open?id=1lLRga6zeGilg1SFfPSJZVqbQHsCYbOzj пунь] Обязательные: 2, 3, 9&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| Кучи&lt;br /&gt;
| [https://codeforces.com/group/iMwjhG4D4C/contest/269940 контест мой контест] Обязательная: A&lt;br /&gt;
| [https://drive.google.com/open?id=1Pdn9bC0HEMvnyzC7bKqC6sXD1drYi2b- домашка моя домашка]&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| Деревья поиска&lt;br /&gt;
| [https://codeforces.com/group/iMwjhG4D4C/contest/272242 коронаконтест] Обязательные задачи: A и D. STL разрешается использовать только в задачах E-G.&lt;br /&gt;
| [https://drive.google.com/open?id=1ftyGP8GgiDZ_lr_9XHCkK0HP_0RD1Fed коронадомашка]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Правила игры ==&lt;br /&gt;
&lt;br /&gt;
* Оценка за экзамен ставится следующим образом:&lt;br /&gt;
** Если вас устраивает накопленная оценка за семестр, то она и выставляется в качестве оценки за экзамен.&lt;br /&gt;
** Если вы хотите поднять итоговую оценку, то вы можете прийти на экзамен и получить за него оценку EXAM от 0 до 10, после чего ваша итоговая оценка будет равна ROUND(0.3*EXAM + 0.7*SEMESTER) .&lt;br /&gt;
* Баллы за семестр набираются за контесты и теор.домашки и конспекты лекций. Всего будет 5-7 контестов и 5-7 теор.домашек.&lt;br /&gt;
** Решённые полностью все контесты и задачи дают вам отл (14). Ваша оценка равна MIN(10, FLOOR((STUDENT_SCORE / MAX_SCORE)  * 14)) - PENALTY .&lt;br /&gt;
** В каждом контесте/листочке есть некоторое количество обязательных задач. Чтобы их сдать, надо получить OK в контесте и защитить решение перед семинаристом или ассистентом на очной сдаче.&lt;br /&gt;
** Дедлайн на устную сдачу: 2 недели от дедлайна контеста/листочка.&lt;br /&gt;
** За незащищённую обязательную задачу ваша итоговая оценка понижается на 1 балл.&lt;br /&gt;
* Конспекты лекций ведутся в системе MediaWiki на этом сайте. Если вы хотите получить 10 бонусных баллов к STUDENT_SCORE и уважение от ваших однокурсников за конспектирование лекций, напишите об этом в телеграм Саше Гришутину (@rationalex). Заявляя своё желание, вы подтверждаете, что готовы законспектировать любую из лекций, на которой вы были (это нужно для того, чтобы равномерно распределить всех желающих по лекциям). При наличии нескольких желающих вести конспект, приоритет будет отдаваться тем, кто получил меньший бонусный балл за ведение конспектов. При равенстве всех пунктов, приоритет будет отдаваться тем, кто до этого сделал конспекты прилежнее и быстрее.&lt;br /&gt;
*Если вы в какой-то момент поймёте, что курс вам неинтересен, то вы можете получить джентльменский уд(3), сдав и защитив все обязательные задачи и больше ничего.&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=%D0%94%D0%9F_%D0%BD%D0%B0_%D0%BF%D0%BE%D0%B4%D0%BC%D0%BD%D0%BE%D0%B6%D0%B5%D1%81%D1%82%D0%B2%D0%B0%D1%85&amp;diff=384</id>
		<title>ДП на подмножествах</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=%D0%94%D0%9F_%D0%BD%D0%B0_%D0%BF%D0%BE%D0%B4%D0%BC%D0%BD%D0%BE%D0%B6%D0%B5%D1%81%D1%82%D0%B2%D0%B0%D1%85&amp;diff=384"/>
				<updated>2020-04-30T16:55:24Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: /* Оптимизация наивного решения с помощью ДП */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Проблема представления подмножеств ==&lt;br /&gt;
Пусть у нас есть некоторое множесьво N = {0, 1, 2, ..., n - 1}, n &amp;amp;le; 30 &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Мы хотим получить ответить на вопрос: &amp;quot; Как эффективно хранить и кодировать подмножества N?&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== Битовые маски ==&lt;br /&gt;
===Что такое битовые маски?===&lt;br /&gt;
Подмножество будем кодировать с помощью двоичного числа, в котором на i-й позиции стоит 1, если i-й элемент множества входит в это подмножество, и 0 в противном случае. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Напрмер, если есть множество А = {3, 5, 7, 9}, то его подмножество B = {3, 7, 9} можно закодировать с помощью маски 1011&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; = 11&amp;lt;sub&amp;gt;10&amp;lt;/sub&amp;gt;. Таким образом, если мы будем кодировать подмножества с помощью десятичного числа типа &amp;lt;code&amp;gt;unsigned int&amp;lt;/code&amp;gt;, то сможем закодировать любое подмножество, размер которого не больше 32х. &lt;br /&gt;
===Как работать с множествами, с помощью масок?===&lt;br /&gt;
&amp;lt;p&amp;gt;Вот так на языке С будет выглядеть функция проверяющая, входит ли элемент множества, стоящий на позиции pos в подмножество с маской mask:&amp;lt;/p&amp;gt;&lt;br /&gt;
    '''bool''' elem_in_subset ('''unsigned int''' mask, '''int''' pos) {&lt;br /&gt;
        return (mask &amp;gt;&amp;gt; pos) &amp;amp; 1;&lt;br /&gt;
    }&lt;br /&gt;
Похожим образом выглядят функции добавления и удаления элемента из подмножества. Для подсчета количества элементов в подмножестве подйдет функция &amp;lt;code&amp;gt;__builtin_popcount (mask)&amp;lt;/code&amp;gt;, которая возвращает количество единичных битов в двоичном представлении mask. Пересечение и объединение подмножеств соответсвует побитовому пересечению и побитовому объединению масок, соответствующих данным подмножествам.&lt;br /&gt;
==ДП на подмножествах==&lt;br /&gt;
===Задача о гамильтоновом цикле===&lt;br /&gt;
1) Пусть есть неориентированный взвешанный граф (можно считать, что между любыми двумя вершинами есть ребро). Необходимо найти [https://ru.wikipedia.org/wiki/%D0%93%D0%BB%D0%BE%D1%81%D1%81%D0%B0%D1%80%D0%B8%D0%B9_%D1%82%D0%B5%D0%BE%D1%80%D0%B8%D0%B8_%D0%B3%D1%80%D0%B0%D1%84%D0%BE%D0%B2#%D0%9F гамильтонов цикл] наименьшего веса.&lt;br /&gt;
====Наивное решение====&lt;br /&gt;
Наивное решение заключается в полном переборе всех гамильтонов циклов и выборе из них цикла с наименьшей стоимостью. Количество циклов равно n! (количество перестановок вершин). Решение можно улучшить до O ($\frac {(n-1)!}{2}$), если заметить, что нам не важно из какой вершины начинать обход и в какую сторону обходиь.&lt;br /&gt;
====Оптимизация наивного решения с помощью ДП====&lt;br /&gt;
Заведем массив &amp;lt;code&amp;gt;dp&amp;lt;/code&amp;gt; такой, что &amp;lt;code&amp;gt;dp[mask][v]&amp;lt;/code&amp;gt; - миннимальная длина пути из 0-й вершины в вершину v, проходящий только по вершинам из множества mask ровно по одному разу.&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=%D0%94%D0%9F_%D0%BD%D0%B0_%D0%BF%D0%BE%D0%B4%D0%BC%D0%BD%D0%BE%D0%B6%D0%B5%D1%81%D1%82%D0%B2%D0%B0%D1%85&amp;diff=383</id>
		<title>ДП на подмножествах</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=%D0%94%D0%9F_%D0%BD%D0%B0_%D0%BF%D0%BE%D0%B4%D0%BC%D0%BD%D0%BE%D0%B6%D0%B5%D1%81%D1%82%D0%B2%D0%B0%D1%85&amp;diff=383"/>
				<updated>2020-04-30T16:49:24Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: /* Задача о гамильтоновом цикле */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Проблема представления подмножеств ==&lt;br /&gt;
Пусть у нас есть некоторое множесьво N = {0, 1, 2, ..., n - 1}, n &amp;amp;le; 30 &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Мы хотим получить ответить на вопрос: &amp;quot; Как эффективно хранить и кодировать подмножества N?&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== Битовые маски ==&lt;br /&gt;
===Что такое битовые маски?===&lt;br /&gt;
Подмножество будем кодировать с помощью двоичного числа, в котором на i-й позиции стоит 1, если i-й элемент множества входит в это подмножество, и 0 в противном случае. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Напрмер, если есть множество А = {3, 5, 7, 9}, то его подмножество B = {3, 7, 9} можно закодировать с помощью маски 1011&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; = 11&amp;lt;sub&amp;gt;10&amp;lt;/sub&amp;gt;. Таким образом, если мы будем кодировать подмножества с помощью десятичного числа типа &amp;lt;code&amp;gt;unsigned int&amp;lt;/code&amp;gt;, то сможем закодировать любое подмножество, размер которого не больше 32х. &lt;br /&gt;
===Как работать с множествами, с помощью масок?===&lt;br /&gt;
&amp;lt;p&amp;gt;Вот так на языке С будет выглядеть функция проверяющая, входит ли элемент множества, стоящий на позиции pos в подмножество с маской mask:&amp;lt;/p&amp;gt;&lt;br /&gt;
    '''bool''' elem_in_subset ('''unsigned int''' mask, '''int''' pos) {&lt;br /&gt;
        return (mask &amp;gt;&amp;gt; pos) &amp;amp; 1;&lt;br /&gt;
    }&lt;br /&gt;
Похожим образом выглядят функции добавления и удаления элемента из подмножества. Для подсчета количества элементов в подмножестве подйдет функция &amp;lt;code&amp;gt;__builtin_popcount (mask)&amp;lt;/code&amp;gt;, которая возвращает количество единичных битов в двоичном представлении mask. Пересечение и объединение подмножеств соответсвует побитовому пересечению и побитовому объединению масок, соответствующих данным подмножествам.&lt;br /&gt;
==ДП на подмножествах==&lt;br /&gt;
===Задача о гамильтоновом цикле===&lt;br /&gt;
1) Пусть есть неориентированный взвешанный граф (можно считать, что между любыми двумя вершинами есть ребро). Необходимо найти [https://ru.wikipedia.org/wiki/%D0%93%D0%BB%D0%BE%D1%81%D1%81%D0%B0%D1%80%D0%B8%D0%B9_%D1%82%D0%B5%D0%BE%D1%80%D0%B8%D0%B8_%D0%B3%D1%80%D0%B0%D1%84%D0%BE%D0%B2#%D0%9F гамильтонов цикл] наименьшего веса.&lt;br /&gt;
====Наивное решение====&lt;br /&gt;
Наивное решение заключается в полном переборе всех гамильтонов циклов и выборе из них цикла с наименьшей стоимостью. Количество циклов равно n! (количество перестановок вершин). Решение можно улучшить до O ($\frac {(n-1)!}{2}$), если заметить, что нам не важно из какой вершины начинать обход и в какую сторону обходиь.&lt;br /&gt;
====Оптимизация наивного решения с помощью ДП====&lt;br /&gt;
    Заведем массив dp.&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=%D0%94%D0%9F_%D0%BD%D0%B0_%D0%BF%D0%BE%D0%B4%D0%BC%D0%BD%D0%BE%D0%B6%D0%B5%D1%81%D1%82%D0%B2%D0%B0%D1%85&amp;diff=382</id>
		<title>ДП на подмножествах</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=%D0%94%D0%9F_%D0%BD%D0%B0_%D0%BF%D0%BE%D0%B4%D0%BC%D0%BD%D0%BE%D0%B6%D0%B5%D1%81%D1%82%D0%B2%D0%B0%D1%85&amp;diff=382"/>
				<updated>2020-04-30T16:47:01Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: /* Задача о гамильтоновом цикле */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Проблема представления подмножеств ==&lt;br /&gt;
Пусть у нас есть некоторое множесьво N = {0, 1, 2, ..., n - 1}, n &amp;amp;le; 30 &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Мы хотим получить ответить на вопрос: &amp;quot; Как эффективно хранить и кодировать подмножества N?&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== Битовые маски ==&lt;br /&gt;
===Что такое битовые маски?===&lt;br /&gt;
Подмножество будем кодировать с помощью двоичного числа, в котором на i-й позиции стоит 1, если i-й элемент множества входит в это подмножество, и 0 в противном случае. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Напрмер, если есть множество А = {3, 5, 7, 9}, то его подмножество B = {3, 7, 9} можно закодировать с помощью маски 1011&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; = 11&amp;lt;sub&amp;gt;10&amp;lt;/sub&amp;gt;. Таким образом, если мы будем кодировать подмножества с помощью десятичного числа типа &amp;lt;code&amp;gt;unsigned int&amp;lt;/code&amp;gt;, то сможем закодировать любое подмножество, размер которого не больше 32х. &lt;br /&gt;
===Как работать с множествами, с помощью масок?===&lt;br /&gt;
&amp;lt;p&amp;gt;Вот так на языке С будет выглядеть функция проверяющая, входит ли элемент множества, стоящий на позиции pos в подмножество с маской mask:&amp;lt;/p&amp;gt;&lt;br /&gt;
    '''bool''' elem_in_subset ('''unsigned int''' mask, '''int''' pos) {&lt;br /&gt;
        return (mask &amp;gt;&amp;gt; pos) &amp;amp; 1;&lt;br /&gt;
    }&lt;br /&gt;
Похожим образом выглядят функции добавления и удаления элемента из подмножества. Для подсчета количества элементов в подмножестве подйдет функция &amp;lt;code&amp;gt;__builtin_popcount (mask)&amp;lt;/code&amp;gt;, которая возвращает количество единичных битов в двоичном представлении mask. Пересечение и объединение подмножеств соответсвует побитовому пересечению и побитовому объединению масок, соответствующих данным подмножествам.&lt;br /&gt;
==ДП на подмножествах==&lt;br /&gt;
===Задача о гамильтоновом цикле===&lt;br /&gt;
1) Пусть есть неориентированный взвешанный граф (можно считать, что между любыми двумя вершинами есть ребро). Необходимо найти [https://ru.wikipedia.org/wiki/%D0%93%D0%BB%D0%BE%D1%81%D1%81%D0%B0%D1%80%D0%B8%D0%B9_%D1%82%D0%B5%D0%BE%D1%80%D0%B8%D0%B8_%D0%B3%D1%80%D0%B0%D1%84%D0%BE%D0%B2#%D0%9F гамильтонов цикл] наименьшего веса.&lt;br /&gt;
====Наивное решение====&lt;br /&gt;
Наивное решение заключается в полном переборе всех гамильтонов циклов и выборе из них цикла с наименьшей стоимостью. Количество циклов равно n! (количество перестановок вершин). Решение можно улучшить до O ($\frac {(n-1)!}{2}$), если заметить, что нам не важно из какой вершины начинать обход и в какую сторону обходиь.&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=%D0%94%D0%9F_%D0%BD%D0%B0_%D0%BF%D0%BE%D0%B4%D0%BC%D0%BD%D0%BE%D0%B6%D0%B5%D1%81%D1%82%D0%B2%D0%B0%D1%85&amp;diff=381</id>
		<title>ДП на подмножествах</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=%D0%94%D0%9F_%D0%BD%D0%B0_%D0%BF%D0%BE%D0%B4%D0%BC%D0%BD%D0%BE%D0%B6%D0%B5%D1%81%D1%82%D0%B2%D0%B0%D1%85&amp;diff=381"/>
				<updated>2020-04-30T16:34:34Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: /* Задача о гамильтоновом цикле */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Проблема представления подмножеств ==&lt;br /&gt;
Пусть у нас есть некоторое множесьво N = {0, 1, 2, ..., n - 1}, n &amp;amp;le; 30 &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Мы хотим получить ответить на вопрос: &amp;quot; Как эффективно хранить и кодировать подмножества N?&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== Битовые маски ==&lt;br /&gt;
===Что такое битовые маски?===&lt;br /&gt;
Подмножество будем кодировать с помощью двоичного числа, в котором на i-й позиции стоит 1, если i-й элемент множества входит в это подмножество, и 0 в противном случае. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Напрмер, если есть множество А = {3, 5, 7, 9}, то его подмножество B = {3, 7, 9} можно закодировать с помощью маски 1011&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; = 11&amp;lt;sub&amp;gt;10&amp;lt;/sub&amp;gt;. Таким образом, если мы будем кодировать подмножества с помощью десятичного числа типа &amp;lt;code&amp;gt;unsigned int&amp;lt;/code&amp;gt;, то сможем закодировать любое подмножество, размер которого не больше 32х. &lt;br /&gt;
===Как работать с множествами, с помощью масок?===&lt;br /&gt;
&amp;lt;p&amp;gt;Вот так на языке С будет выглядеть функция проверяющая, входит ли элемент множества, стоящий на позиции pos в подмножество с маской mask:&amp;lt;/p&amp;gt;&lt;br /&gt;
    '''bool''' elem_in_subset ('''unsigned int''' mask, '''int''' pos) {&lt;br /&gt;
        return (mask &amp;gt;&amp;gt; pos) &amp;amp; 1;&lt;br /&gt;
    }&lt;br /&gt;
Похожим образом выглядят функции добавления и удаления элемента из подмножества. Для подсчета количества элементов в подмножестве подйдет функция &amp;lt;code&amp;gt;__builtin_popcount (mask)&amp;lt;/code&amp;gt;, которая возвращает количество единичных битов в двоичном представлении mask. Пересечение и объединение подмножеств соответсвует побитовому пересечению и побитовому объединению масок, соответствующих данным подмножествам.&lt;br /&gt;
==ДП на подмножествах==&lt;br /&gt;
===Задача о гамильтоновом цикле===&lt;br /&gt;
1) Пусть есть неориентированный взвешанный граф (можно считать, что между любыми двумя вершинами есть ребро). Необходимо найти [https://ru.wikipedia.org/wiki/%D0%93%D0%BB%D0%BE%D1%81%D1%81%D0%B0%D1%80%D0%B8%D0%B9_%D1%82%D0%B5%D0%BE%D1%80%D0%B8%D0%B8_%D0%B3%D1%80%D0%B0%D1%84%D0%BE%D0%B2#%D0%9F Гамильтонов путь] наименьшего веса.&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=%D0%94%D0%9F_%D0%BD%D0%B0_%D0%BF%D0%BE%D0%B4%D0%BC%D0%BD%D0%BE%D0%B6%D0%B5%D1%81%D1%82%D0%B2%D0%B0%D1%85&amp;diff=380</id>
		<title>ДП на подмножествах</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=%D0%94%D0%9F_%D0%BD%D0%B0_%D0%BF%D0%BE%D0%B4%D0%BC%D0%BD%D0%BE%D0%B6%D0%B5%D1%81%D1%82%D0%B2%D0%B0%D1%85&amp;diff=380"/>
				<updated>2020-04-30T16:33:52Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: /* Задача о гамильтоновом цикле */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Проблема представления подмножеств ==&lt;br /&gt;
Пусть у нас есть некоторое множесьво N = {0, 1, 2, ..., n - 1}, n &amp;amp;le; 30 &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Мы хотим получить ответить на вопрос: &amp;quot; Как эффективно хранить и кодировать подмножества N?&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== Битовые маски ==&lt;br /&gt;
===Что такое битовые маски?===&lt;br /&gt;
Подмножество будем кодировать с помощью двоичного числа, в котором на i-й позиции стоит 1, если i-й элемент множества входит в это подмножество, и 0 в противном случае. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Напрмер, если есть множество А = {3, 5, 7, 9}, то его подмножество B = {3, 7, 9} можно закодировать с помощью маски 1011&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; = 11&amp;lt;sub&amp;gt;10&amp;lt;/sub&amp;gt;. Таким образом, если мы будем кодировать подмножества с помощью десятичного числа типа &amp;lt;code&amp;gt;unsigned int&amp;lt;/code&amp;gt;, то сможем закодировать любое подмножество, размер которого не больше 32х. &lt;br /&gt;
===Как работать с множествами, с помощью масок?===&lt;br /&gt;
&amp;lt;p&amp;gt;Вот так на языке С будет выглядеть функция проверяющая, входит ли элемент множества, стоящий на позиции pos в подмножество с маской mask:&amp;lt;/p&amp;gt;&lt;br /&gt;
    '''bool''' elem_in_subset ('''unsigned int''' mask, '''int''' pos) {&lt;br /&gt;
        return (mask &amp;gt;&amp;gt; pos) &amp;amp; 1;&lt;br /&gt;
    }&lt;br /&gt;
Похожим образом выглядят функции добавления и удаления элемента из подмножества. Для подсчета количества элементов в подмножестве подйдет функция &amp;lt;code&amp;gt;__builtin_popcount (mask)&amp;lt;/code&amp;gt;, которая возвращает количество единичных битов в двоичном представлении mask. Пересечение и объединение подмножеств соответсвует побитовому пересечению и побитовому объединению масок, соответствующих данным подмножествам.&lt;br /&gt;
==ДП на подмножествах==&lt;br /&gt;
===Задача о гамильтоновом цикле===&lt;br /&gt;
1) Пусть есть неориентированный взвешанный граф (можно считать, что между любыми двумя вершинами есть ребро). Необходимо найти [https://ru.wikipedia.org/wiki/%D0%93%D0%BB%D0%BE%D1%81%D1%81%D0%B0%D1%80%D0%B8%D0%B9_%D1%82%D0%B5%D0%BE%D1%80%D0%B8%D0%B8_%D0%B3%D1%80%D0%B0%D1%84%D0%BE%D0%B2#%D0%9F Гамильтонов путь] наименьшего веса (простой путь, проходящий через каждую вершину ровно один раз).&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=%D0%94%D0%9F_%D0%BD%D0%B0_%D0%BF%D0%BE%D0%B4%D0%BC%D0%BD%D0%BE%D0%B6%D0%B5%D1%81%D1%82%D0%B2%D0%B0%D1%85&amp;diff=379</id>
		<title>ДП на подмножествах</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=%D0%94%D0%9F_%D0%BD%D0%B0_%D0%BF%D0%BE%D0%B4%D0%BC%D0%BD%D0%BE%D0%B6%D0%B5%D1%81%D1%82%D0%B2%D0%B0%D1%85&amp;diff=379"/>
				<updated>2020-04-30T16:24:32Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Проблема представления подмножеств ==&lt;br /&gt;
Пусть у нас есть некоторое множесьво N = {0, 1, 2, ..., n - 1}, n &amp;amp;le; 30 &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Мы хотим получить ответить на вопрос: &amp;quot; Как эффективно хранить и кодировать подмножества N?&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== Битовые маски ==&lt;br /&gt;
===Что такое битовые маски?===&lt;br /&gt;
Подмножество будем кодировать с помощью двоичного числа, в котором на i-й позиции стоит 1, если i-й элемент множества входит в это подмножество, и 0 в противном случае. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Напрмер, если есть множество А = {3, 5, 7, 9}, то его подмножество B = {3, 7, 9} можно закодировать с помощью маски 1011&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; = 11&amp;lt;sub&amp;gt;10&amp;lt;/sub&amp;gt;. Таким образом, если мы будем кодировать подмножества с помощью десятичного числа типа &amp;lt;code&amp;gt;unsigned int&amp;lt;/code&amp;gt;, то сможем закодировать любое подмножество, размер которого не больше 32х. &lt;br /&gt;
===Как работать с множествами, с помощью масок?===&lt;br /&gt;
&amp;lt;p&amp;gt;Вот так на языке С будет выглядеть функция проверяющая, входит ли элемент множества, стоящий на позиции pos в подмножество с маской mask:&amp;lt;/p&amp;gt;&lt;br /&gt;
    '''bool''' elem_in_subset ('''unsigned int''' mask, '''int''' pos) {&lt;br /&gt;
        return (mask &amp;gt;&amp;gt; pos) &amp;amp; 1;&lt;br /&gt;
    }&lt;br /&gt;
Похожим образом выглядят функции добавления и удаления элемента из подмножества. Для подсчета количества элементов в подмножестве подйдет функция &amp;lt;code&amp;gt;__builtin_popcount (mask)&amp;lt;/code&amp;gt;, которая возвращает количество единичных битов в двоичном представлении mask. Пересечение и объединение подмножеств соответсвует побитовому пересечению и побитовому объединению масок, соответствующих данным подмножествам.&lt;br /&gt;
==ДП на подмножествах==&lt;br /&gt;
===Задача о гамильтоновом цикле===&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	<entry>
		<id>http://wiki.atp-fivt.org/index.php?title=%D0%94%D0%9F_%D0%BD%D0%B0_%D0%BF%D0%BE%D0%B4%D0%BC%D0%BD%D0%BE%D0%B6%D0%B5%D1%81%D1%82%D0%B2%D0%B0%D1%85&amp;diff=378</id>
		<title>ДП на подмножествах</title>
		<link rel="alternate" type="text/html" href="http://wiki.atp-fivt.org/index.php?title=%D0%94%D0%9F_%D0%BD%D0%B0_%D0%BF%D0%BE%D0%B4%D0%BC%D0%BD%D0%BE%D0%B6%D0%B5%D1%81%D1%82%D0%B2%D0%B0%D1%85&amp;diff=378"/>
				<updated>2020-04-30T16:23:31Z</updated>
		
		<summary type="html">&lt;p&gt;Algocourselecturenotes: /* Описание проблемы */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Проблема представления подмножеств ==&lt;br /&gt;
Пусть у нас есть некоторое множесьво N = {0, 1, 2, ..., n - 1}, n &amp;amp;le; 30 &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Мы хотим получить ответить на вопрос: &amp;quot; Как эффективно хранить и кодировать подмножества N?&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== Битовые маски ==&lt;br /&gt;
===Что такое битовые маски?===&lt;br /&gt;
Подмножество будем кодировать с помощью двоичного числа, в котором на i-й позиции стоит 1, если i-й элемент множества входит в это подмножество, и 0 в противном случае. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Напрмер, если есть множество А = {3, 5, 7, 9}, то его подмножество B = {3, 7, 9} можно закодировать с помощью маски 1011&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; = 11&amp;lt;sub&amp;gt;10&amp;lt;/sub&amp;gt;. Таким образом, если мы будем кодировать подмножества с помощью десятичного числа типа &amp;lt;code&amp;gt;unsigned int&amp;lt;/code&amp;gt;, то сможем закодировать любое подмножество, размер которого не больше 32х. &lt;br /&gt;
===Как работать с множествами, с помощью масок?===&lt;br /&gt;
&amp;lt;p&amp;gt;Вот так на языке С будет выглядеть функция проверяющая, входит ли элемент множества, стоящий на позиции pos в подмножество с маской mask:&amp;lt;/p&amp;gt;&lt;br /&gt;
    '''bool''' elem_in_subset ('''unsigned int''' mask, '''int''' pos) {&lt;br /&gt;
        return (mask &amp;gt;&amp;gt; pos) &amp;amp; 1;&lt;br /&gt;
    }&lt;br /&gt;
Похожим образом выглядят функции добавления и удаления элемента из подмножества. Для подсчета количества элементов в подмножестве подйдет функция &amp;lt;code&amp;gt;__builtin_popcount (mask)&amp;lt;/code&amp;gt;, которая возвращает количество единичных битов в двоичном представлении mask. Пересечение и объединение подмножеств соответсвует побитовому пересечению и побитовому объединению масок, соответствующих данным подмножествам.&lt;/div&gt;</summary>
		<author><name>Algocourselecturenotes</name></author>	</entry>

	</feed>