<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ru">
	<id>https://acm.khpnets.info/w39/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Ctrlalt</id>
	<title>Олимпиадное программирование в УлГТУ - Вклад [ru]</title>
	<link rel="self" type="application/atom+xml" href="https://acm.khpnets.info/w39/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Ctrlalt"/>
	<link rel="alternate" type="text/html" href="https://acm.khpnets.info/wiki/%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/Ctrlalt"/>
	<updated>2026-05-13T11:36:29Z</updated>
	<subtitle>Вклад</subtitle>
	<generator>MediaWiki 1.39.3</generator>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=%D0%A1%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D0%B0_%D0%BD%D0%B5%D0%BF%D0%B5%D1%80%D0%B5%D1%81%D0%B5%D0%BA%D0%B0%D1%8E%D1%89%D0%B8%D1%85%D1%81%D1%8F_%D0%BC%D0%BD%D0%BE%D0%B6%D0%B5%D1%81%D1%82%D0%B2&amp;diff=2950</id>
		<title>Система непересекающихся множеств</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=%D0%A1%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D0%B0_%D0%BD%D0%B5%D0%BF%D0%B5%D1%80%D0%B5%D1%81%D0%B5%D0%BA%D0%B0%D1%8E%D1%89%D0%B8%D1%85%D1%81%D1%8F_%D0%BC%D0%BD%D0%BE%D0%B6%D0%B5%D1%81%D1%82%D0%B2&amp;diff=2950"/>
		<updated>2026-04-25T19:46:01Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Общие сведения ==&lt;br /&gt;
Система неперескающихся множеств (англ. disjointed set union, иногда union-find) — специфическая структура данных, содержащая информацию о наборе множеств, которая позволяет объединять множества и отвечать на вопрос, принадлежат ли указанные элементы к одному множеству.&lt;br /&gt;
&lt;br /&gt;
Назначение системы непересекающихся множеств позволяют понять такие метафоры, как задача о постройке дорог между городами, где периодически требуется отвечать на запросы о достижимости одного города из другого, и задача о добавлении друзей в социальной сети, где требуется узнавать, связаны ли два человека цепочкой общих друзей.&lt;br /&gt;
&lt;br /&gt;
Изначально рассматриваются N различных элементов, каждый из которых представляет собой самостоятельное множество. Далее любые два множества допустимо объединять, при этом все элементы обоих множеств становятся элементами результирующего множества. Очевидно, что из начального состояния (N одноэлементных множеств) через (N-1) слияние будет получено состояние, при котором все элементы объединены в одно множество. Как можно будет убедиться в дальнейшем, реализации системы непересекающихся множеств достаточно просто дополнить операцией добавления нового одноэлементного множества (то есть добавления (N+1)-го элемента, формирующего самостоятельное множество).&lt;br /&gt;
&lt;br /&gt;
Любое множество может быть уникальным образом идентифицировано с помощью одного из своих элементов: два множества не могут содержать один и тот же элемент по определению (этот факт отражён словом «непересекающихся» в названии структуры данных). Такой элемент называется &#039;&#039;представителем&#039;&#039; множества (англ. representative element).&lt;br /&gt;
&lt;br /&gt;
== Интерфейс ==&lt;br /&gt;
Здесь и далее будем предполагать, что элементами множеств являются целые числа. Система непересекающихся множеств должна обеспечивать следующие операции:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;methodlist&amp;quot;&lt;br /&gt;
| ||  int || find(int x)             || — определение представителя множества, которому принадлежит элемент &amp;lt;tt&amp;gt;x&amp;lt;/tt&amp;gt;. Очевидно, что если элементы &amp;lt;tt&amp;gt;x&amp;lt;/tt&amp;gt; и &amp;lt;tt&amp;gt;y&amp;lt;/tt&amp;gt; принадлежат одному множеству, то должно выполняться условие &amp;lt;tt&amp;gt;find(x) == find(y)&amp;lt;/tt&amp;gt;. В противном случае должно иметь место &amp;lt;tt&amp;gt;find(x) != find(y)&amp;lt;/tt&amp;gt;;&lt;br /&gt;
|- &lt;br /&gt;
| || void || merge(int x, int y)     || — слияние множеств, содержащих элементы &amp;lt;tt&amp;gt;x&amp;lt;/tt&amp;gt; и &amp;lt;tt&amp;gt;y&amp;lt;/tt&amp;gt;;&lt;br /&gt;
|-&lt;br /&gt;
| || bool || connected(int x, int y) || — определение принадлежности элементов &amp;lt;tt&amp;gt;x&amp;lt;/tt&amp;gt; и &amp;lt;tt&amp;gt;y&amp;lt;/tt&amp;gt; к одному множеству. Из определения метода &amp;lt;tt&amp;gt;find()&amp;lt;/tt&amp;gt; следует, что данный метод в любых реализациях может иметь единый код: &amp;lt;tt&amp;gt;return find(x) == find(y);&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Демонстрация работы ==&lt;br /&gt;
* [http://www.cs.usfca.edu/~galles/visualization/DisjointSets.html Data Structure Visualizations &amp;amp;mdash; Disjoint Sets]&lt;br /&gt;
: В демонстрации элементы-представители содержат ссылки не на самих себя, а на несуществующую ячейку (-1).&lt;br /&gt;
* [http://visualgo.net/ufds.html VisuAlgo &amp;amp;mdash; Union-Find Disjoint Sets]&lt;br /&gt;
&lt;br /&gt;
== Простейшая реализация: быстрое определение представителя ==&lt;br /&gt;
[[Файл:dsu_quickfind_representation.png|thumb|right|360px|Система непересекающихся множеств в простейшей реализации]]&lt;br /&gt;
Определим массив &amp;lt;tt&amp;gt;id[]&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;i&amp;lt;/tt&amp;gt;-ая ячейка которого будет содержать представителя множества, которому принадлежит элемент &amp;lt;tt&amp;gt;i&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Изначально каждый элемент системы непересекающихся множеств сам по себе рассматривается как одноэлементное множество, поэтому каждый элемент является собственным представителем. Показанный ниже конструктор использует динамический массив (вектор) для создания системы из &amp;lt;tt&amp;gt;n&amp;lt;/tt&amp;gt; непересекающихся множеств.&lt;br /&gt;
&lt;br /&gt;
 DSU(int n) {&lt;br /&gt;
     for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
         id.push_back(i); //id[i] == i;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== Определение представителя ===&lt;br /&gt;
[[Файл:dsu_quickfind_find.png|thumb|right|360px|Быстрое определение представителя]]&lt;br /&gt;
Как показано выше, массив &amp;lt;tt&amp;gt;id[]&amp;lt;/tt&amp;gt; содержит в своих ячейках значения представителей для всех элементов. Поэтому для вывода представителя в данной реализации достаточно вернуть значение соответствующей ячейки массива &amp;lt;tt&amp;gt;id[]&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 int find(int x) {&lt;br /&gt;
     return id[x];&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Можно видеть, что сложность операции определения представителя в этой реализации — O(1).&lt;br /&gt;
&lt;br /&gt;
=== Объединение множеств ===&lt;br /&gt;
[[Файл:dsu_quickfind_union.png|thumb|right|360px|Объединение множеств в простейшей реализации]]&lt;br /&gt;
После слияния множеств должна сохраняться работоспособность операции &amp;lt;tt&amp;gt;find()&amp;lt;/tt&amp;gt;, то есть все элементы объединённого множества должны иметь одного и того же представителя. Так как до объединения часть элементов имеет представителя &amp;lt;tt&amp;gt;a&amp;lt;/tt&amp;gt;, а другая часть — представителя &amp;lt;tt&amp;gt;b&amp;lt;/tt&amp;gt;, проще всего изменить все упоминания &amp;lt;tt&amp;gt;a&amp;lt;/tt&amp;gt; на &amp;lt;tt&amp;gt;b&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
 void merge(int x, int y) {&lt;br /&gt;
     int rx = find(x), ry = find(y);&lt;br /&gt;
     if (rx == ry)&lt;br /&gt;
         return;&lt;br /&gt;
     for (int i = 0; i &amp;lt; id.size(); i++)&lt;br /&gt;
         if (id[i] == rx)&lt;br /&gt;
             id[i] = ry;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
Таким образом, объединение требует прохода по всему массиву, что увеличивает его сложность до O(N). Данное значение для многих задач является неудовлетворительным, поэтому разработаны реализации, позволяющие осуществлять объединение множеств с меньшей асимптотикой.&lt;br /&gt;
&lt;br /&gt;
== Вторая реализация: быстрое объединение ==&lt;br /&gt;
[[Файл:dsu_quickunion_representation.png|thumb|right|360px|Система непересекающихся множеств: реализация для быстрого объединения]]&lt;br /&gt;
Идея данной реализации в том, что при объединении множеств изменяется лишь один элемент массива &amp;lt;tt&amp;gt;id[]&amp;lt;/tt&amp;gt;. Чтобы это стало возможным, прежде всего необходимо определить несколько условий: &lt;br /&gt;
&lt;br /&gt;
*Теперь элементы множества будут представлены в некоторой многоуровневой древовидной структуре;&lt;br /&gt;
*&amp;lt;tt&amp;gt;i&amp;lt;/tt&amp;gt;-ая ячейка массива &amp;lt;tt&amp;gt;id[]&amp;lt;/tt&amp;gt; будет содержать значение элемента, к которому в этой структуре присоединён элемент &amp;lt;tt&amp;gt;i&amp;lt;/tt&amp;gt;;&lt;br /&gt;
*Если некоторый элемент &amp;lt;tt&amp;gt;i&amp;lt;/tt&amp;gt; не присоединён к другому элементу, то выполняется &amp;lt;tt&amp;gt;id[i] == i&amp;lt;/tt&amp;gt;. Очевидно, что в каждой древовидной структуре найдётся элемент, обладающий таким свойством — это корень древовидной структуры. Важно, что от любого элемента структуры можно дойти до корневого элемента через некоторое конечное число «подъёмов» по массиву &amp;lt;tt&amp;gt;id[]&amp;lt;/tt&amp;gt;. Эта особенность позволяет использовать корень древовидной структуры в качестве представителя множества.&lt;br /&gt;
&lt;br /&gt;
Конструктор системы непересекающихся множеств остаётся прежним, так как в одноэлементном множестве единственный элемент будет являться корнем и в массиве &amp;lt;tt&amp;gt;id[]&amp;lt;/tt&amp;gt; будет указывать на себя.&lt;br /&gt;
&lt;br /&gt;
 DSU(int n) {&lt;br /&gt;
     for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
         id.push_back(i); //id[i] == i;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== Определение представителя ===&lt;br /&gt;
[[Файл:dsu_quickunion_find.png|thumb|right|360px|Определение представителя как корневого элемента]]&lt;br /&gt;
Так как представитель множества — это корень его древовидной структуры, требуется определить именно его. Для этого нужно воспользоваться свойством корня — тем фактом, что он указывает на себя.&lt;br /&gt;
&lt;br /&gt;
 int find(int x) {&lt;br /&gt;
     if (id[x] == x)&lt;br /&gt;
         return x;&lt;br /&gt;
     return find(id[x]);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Определение представителя в худшем случае следует по массиву &amp;lt;tt&amp;gt;id[]&amp;lt;/tt&amp;gt; от элемента-листа до корня древовидной струтуры, поэтому её сложность прямо пропорциональна высоте древовидной структуры. Ниже показано, что эта сложность составляет O(N).&lt;br /&gt;
&lt;br /&gt;
=== Объединение множеств ===&lt;br /&gt;
[[Файл:dsu_quickunion_union.png|thumb|right|360px|Быстрое объединение множеств]]&lt;br /&gt;
В рассмотренных ограничениях объединение множеств представляет собой объединение их древовидных структур. Для выполнения последнего достаточно присоединить корень одной структуры к элементу другой структуры; в целях минимального увеличения высоты результирующей структуры наиболее выгодно присоединять корень одной из структур к корню другой. Как упоминалось ранее, это потребует корректировки всего одного элемента массива &amp;lt;tt&amp;gt;id[]&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
 void merge(int x, int y) {&lt;br /&gt;
     int rx = find(x), ry = find(y);&lt;br /&gt;
     if (rx != ry)&lt;br /&gt;
         id[rx] = ry;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Несмотря на то, что операция объединения содержит лишь одно обращение к массиву &amp;lt;tt&amp;gt;id[]&amp;lt;/tt&amp;gt;, она использует вызовы операции &amp;lt;tt&amp;gt;find()&amp;lt;/tt&amp;gt;, поэтому её сложность также зависит от высоты древовидной структуры. В том случае, когда происходит постоянное подвешивание корня структуры к единственному элементу другой структуры, результат вырождается в односвязный список, а его высота становится равной N. Поэтому операция объединения, равно как операция определения представителя, в данной реализации имеет сложность O(N).&lt;br /&gt;
&lt;br /&gt;
С помощью двух сравнительно простых дополнений скорость работы данной реализации улучшается разительным образом.&lt;br /&gt;
&lt;br /&gt;
== Оптимальная реализация ==&lt;br /&gt;
Реализация системы непересекающихся множеств с наилучшим возможным временем работы осуществляется на базе версии «быстрого объединения» с использованием двух дополнительных эвристик (оптимизаций, не имеющих строгого математического обоснования).&lt;br /&gt;
&lt;br /&gt;
=== Эвристика объединения по рангу ===&lt;br /&gt;
[[Файл:dsu_weighted_union.png|thumb|right|Принцип объединения по рангу]]&lt;br /&gt;
Как было показано ранее, время работы реализации «быстрое объединение» ухудшается с ростом высоты древовидной структуры. Сама же высота может стремительно деградировать до N при неудачно подобранных входных данных (когда структура большего размера присоединяется к корню структуры меньшго размера). Данная оптимизация служит для исключения подобных ситуаций и вводит новое правило: структура большего размера не может быть присоедина к структуре меньшего размера.&lt;br /&gt;
 &lt;br /&gt;
Для отслеживания размера структур определяется массив &amp;lt;tt&amp;gt;size[]&amp;lt;/tt&amp;gt;, элементы которого в конструкторе инициализируются единицами (так как исходные множества являются одноэлементными):&lt;br /&gt;
&lt;br /&gt;
 DSU(int n) {&lt;br /&gt;
     for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
         id.push_back(i); //id[i] == i&lt;br /&gt;
         size.push_back(1); //size[i] == 1&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Код операции &amp;lt;tt&amp;gt;merge()&amp;lt;/tt&amp;gt; изменяется соответствующим образом: решение о том, какая из структур будет присоединена к другой, осуществяется на основании сравнения их размеров. Размер результирующей структуры соответствующим образом увеличивается на количество присоединённых элементов:&lt;br /&gt;
&lt;br /&gt;
 void merge(int x, int y) {&lt;br /&gt;
     int rx = find(x), ry = find(y);&lt;br /&gt;
     if (rx == ry)&lt;br /&gt;
         return;&lt;br /&gt;
     if (size[rx] &amp;lt; size[ry]) {&lt;br /&gt;
         id[rx] = ry;&lt;br /&gt;
         size[ry] += size[rx];&lt;br /&gt;
     } else {&lt;br /&gt;
         id[ry] = rx;&lt;br /&gt;
         size[rx] += size[ry];&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
После данной оптимизации высота дерева увеличивается на 1 только в результате слияния двух множеств равного размера. Так как таких слияний для N элементов может произойти не более log&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt;N, высота дерева в худшем случае оптимизируется до logN.&lt;br /&gt;
&lt;br /&gt;
В качестве критерия присоединения можно использовать не только размер, но и высоту древовидных структур. Более того, часто можно достичь удовлетворительной производительности только за счёт случайного выбора присоединяемой структуры (применение классической идеи рандомизированного алгоритма).&lt;br /&gt;
&lt;br /&gt;
=== Эвристика сжатия путей ===&lt;br /&gt;
[[Файл:dsu_path_compressing.png|thumb|right|360px|Сжатие путей при выполнении операции find]]&lt;br /&gt;
Дополнительно уменьшить высоту дерева можно, если в операции &amp;lt;tt&amp;gt;find()&amp;lt;/tt&amp;gt; перенаправлять все посещённые вершины непосредственно на найденный корень:&lt;br /&gt;
&lt;br /&gt;
 int find(int x) {&lt;br /&gt;
     if (id[x] == x)&lt;br /&gt;
         return x;&lt;br /&gt;
     return id[x] = find(id[x]);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Данная оптимизация вкупе с предыдущей делает дерево практически плоским, что обеспечивает асимптотику операций, почти равную O(1).&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Ниже приведён полный код оптимальной реализации системы непересекающихся множеств.&lt;br /&gt;
&lt;br /&gt;
 class DSU {&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;int&amp;gt; id;&lt;br /&gt;
     vector&amp;lt;int&amp;gt; size;&lt;br /&gt;
 &lt;br /&gt;
 public:&lt;br /&gt;
 &lt;br /&gt;
     DSU(int n) {&lt;br /&gt;
         for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
             id.push_back(i);&lt;br /&gt;
             size.push_back(1);&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     int find(int x) {&lt;br /&gt;
         if (id[x] == x)&lt;br /&gt;
             return x;&lt;br /&gt;
         return id[x] = find(id[x]);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     void merge(int x, int y) {&lt;br /&gt;
         int rx = find(x), ry = find(y);&lt;br /&gt;
         if (rx == ry)&lt;br /&gt;
             return;&lt;br /&gt;
         if (size[rx] &amp;lt; size[ry]) {&lt;br /&gt;
             id[rx] = ry;&lt;br /&gt;
             size[ry] += size[rx];&lt;br /&gt;
         } else {&lt;br /&gt;
             id[ry] = rx;&lt;br /&gt;
             size[ry] += size[rx];&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     bool connected(int x, int y) {&lt;br /&gt;
         return find(x) == find(y);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
Некоторые вопросы для размышления:&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;Каким образом реализуется операция &amp;lt;tt&amp;gt;makeSet()&amp;lt;/tt&amp;gt;, добавляющая в систему новое одноэлементное множество?&#039;&#039;&lt;br /&gt;
*&#039;&#039;Как реализовать эвристику объединения по рангу, использующую в качестве критерия высоту древовидных структур?&#039;&#039;&lt;br /&gt;
*&#039;&#039;Обратите внимание: после выполнения метода &amp;lt;tt&amp;gt;find()&amp;lt;/tt&amp;gt;, использующего эвристику сжатия путей, некоторые элементы массива &amp;lt;tt&amp;gt;size[]&amp;lt;/tt&amp;gt; могут содержать неверную (неактуальную) информацию. Влияет ли это на правильность объединения по рангу? Нужно ли исправлять это несоответствие?&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Задачи, рашаемые с помощью системы непересекающихся множеств ==&lt;br /&gt;
Большинство задач, связанных с системой непересекающихся множеств, относятся к так называемым &#039;&#039;оффлайн-задачам&#039;&#039;, когда все запросы и их порядок известны заранее. В этом случае допустимо считать и проанализировать все запросы, а также получать ответы на них в порядке, отличном от порядка поступления запросов.&lt;br /&gt;
&lt;br /&gt;
*Расчёт различных функций (сумма элементов, максимум и т. п.) на множествах. Достаточно определить отдельные массивы для значений данных функций аналогично массиву &amp;lt;tt&amp;gt;size[]&amp;lt;/tt&amp;gt;, должным образом инициализировать их элементы и обновлять их при слиянии множеств;&lt;br /&gt;
*Задача о разрезании графа (запросы об удалении рёбер и принадлежности вершин одной компоненте связности) в оффлайн. Можно считать запросы и выполнять их в обратном порядке (что будет аналогично добавлению рёбер). Полученная задача аналогична тем, которые приведены во введении к данной статье.&lt;br /&gt;
*Определение минимального остовного дерева в оффлайн алгоритмом Крускала;&lt;br /&gt;
*Определение ближайшего общего предка в оффлайн алгоритмом Тарьяна.&lt;br /&gt;
&lt;br /&gt;
== Код ==&lt;br /&gt;
&lt;br /&gt;
 struct DSU {&lt;br /&gt;
     vector&amp;lt;int&amp;gt; parent;&lt;br /&gt;
 &lt;br /&gt;
     DSU(int vertexCount) {&lt;br /&gt;
         for (int v = 0; v &amp;lt; vertexCount; v++)&lt;br /&gt;
             parent.push_back(v);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     int findRoot(int v) {&lt;br /&gt;
         return parent[v] == v ? v : parent[v] = findRoot(parent[v]);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     bool areConnected(int a, int b) {&lt;br /&gt;
         return findRoot(a) == findRoot(b);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     void connect(int a, int b) {&lt;br /&gt;
         int rootA = findRoot(a);&lt;br /&gt;
         int rootB = findRoot(b);&lt;br /&gt;
         if (rootA == rootB)&lt;br /&gt;
             return;&lt;br /&gt;
         if (rand() % 2)&lt;br /&gt;
             parent[rootA] = rootB;&lt;br /&gt;
         else&lt;br /&gt;
             parent[rootB] = rootA;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
Теория:&lt;br /&gt;
* [https://codeforces.com/edu/course/2/lesson/7 Codeforces EDU — Система непересекающихся множеств]&lt;br /&gt;
* [http://e-maxx.ru/algo/dsu e-maxx.ru &amp;amp;mdash; Система непересекающихся множеств]&lt;br /&gt;
* [http://neerc.ifmo.ru/wiki/index.php?title=%D0%94%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%B0%D1%8F_%D0%BC%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0,_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D1%8B_%D0%B8_%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D1%8B_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85#.D0.A1.D0.B8.D1.81.D1.82.D0.B5.D0.BC.D0.B0_.D0.BD.D0.B5.D0.BF.D0.B5.D1.80.D0.B5.D1.81.D0.B5.D0.BA.D0.B0.D1.8E.D1.89.D0.B8.D1.85.D1.81.D1.8F_.D0.BC.D0.BD.D0.BE.D0.B6.D0.B5.D1.81.D1.82.D0.B2 neerc.ifmo.ru/wiki &amp;amp;mdash; Система непересекающихся множеств]&lt;br /&gt;
* [https://brestprog.by/topics/dsu/ Brestprog — Система непересекающихся множеств (DSU)]&lt;br /&gt;
* [http://habrahabr.ru/post/104772/ habrahabr.ru &amp;amp;mdash; Система непересекающихся множеств и её применения]&lt;br /&gt;
* [http://cppalgo.blogspot.ru/2011/10/disjoint-set-union.html cppalgo.blogspot.ru &amp;amp;mdash; Система непересекающихся множеств]&lt;br /&gt;
* [https://www.youtube.com/watch?v=gfSpPbJWzVs algs4.cs.princeton.edu/lectures — 1.5 Union Find] ([https://algs4.cs.princeton.edu/lectures/keynote/15UnionFind.pdf slides])&lt;br /&gt;
Код:&lt;br /&gt;
* [https://github.com/indy256/codelibrary/blob/master/cpp/structures/disjoint_sets_ranked.cpp CodeLibrary &amp;amp;mdash; Disjoint-set data structure]&lt;br /&gt;
* algs4.cs.princeton.edu/code &amp;amp;mdash; [http://algs4.cs.princeton.edu/code/edu/princeton/cs/algs4/QuickFindUF.java.html quick find], [http://algs4.cs.princeton.edu/code/edu/princeton/cs/algs4/QuickUnionUF.java.html quick union], [http://algs4.cs.princeton.edu/code/edu/princeton/cs/algs4/WeightedQuickUnionUF.java.html weighted quick union], [http://algs4.cs.princeton.edu/code/edu/princeton/cs/algs4/UF.java.html union-by-rank with path halving]&lt;br /&gt;
Задачи:&lt;br /&gt;
* [http://informatics.mccme.ru/course/view.php?id=18 informatics.mccme.ru &amp;amp;mdash; Курс &amp;amp;laquo;Структуры данных&amp;amp;raquo; &amp;amp;mdash; часть 7]&lt;br /&gt;
* [[:Категория:Задачи: Система непересекающихся множеств|Задачи: Система непересекающихся множеств]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Базовые структуры и абстрактные типы данных]]&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=%D0%A1%D1%83%D1%84%D1%84%D0%B8%D0%BA%D1%81%D0%BD%D1%8B%D0%B9_%D0%BC%D0%B0%D1%81%D1%81%D0%B8%D0%B2&amp;diff=2949</id>
		<title>Суффиксный массив</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=%D0%A1%D1%83%D1%84%D1%84%D0%B8%D0%BA%D1%81%D0%BD%D1%8B%D0%B9_%D0%BC%D0%B0%D1%81%D1%81%D0%B8%D0%B2&amp;diff=2949"/>
		<updated>2026-04-11T15:37:04Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Построение ==&lt;br /&gt;
&lt;br /&gt;
Построение за Nlog&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;N:&lt;br /&gt;
 vector&amp;lt;int&amp;gt; makeSuffixArray(string s) {&lt;br /&gt;
     s += &#039;\0&#039;;&lt;br /&gt;
     int n = s.size();&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;int&amp;gt; p(n);&lt;br /&gt;
     for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
         p[i] = i;&lt;br /&gt;
     sort(p.begin(), p.end(), [&amp;amp;](int i, int j) { return s[i] &amp;lt; s[j]; });&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;int&amp;gt; rank(n);&lt;br /&gt;
     rank[p[0]] = 0;&lt;br /&gt;
     for (int i = 1; i &amp;lt; n; i++)&lt;br /&gt;
         rank[p[i]] = rank[p[i - 1]] + (s[p[i]] != s[p[i - 1]]);&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; rankPair(n);&lt;br /&gt;
     for (int len = 1; len &amp;lt; n; len *= 2) {&lt;br /&gt;
         for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
             rankPair[i] = { rank[i], rank[(i + len) % n] };&lt;br /&gt;
 &lt;br /&gt;
         sort(p.begin(), p.end(), [&amp;amp;](int i, int j) { return rankPair[i] &amp;lt; rankPair[j]; });&lt;br /&gt;
 &lt;br /&gt;
         rank[p[0]] = 0;&lt;br /&gt;
         for (int i = 1; i &amp;lt; n; i++)&lt;br /&gt;
             rank[p[i]] = rank[p[i - 1]] + (rankPair[p[i]] != rankPair[p[i - 1]]);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return p;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Построение за NlogN:&lt;br /&gt;
&lt;br /&gt;
Меняем вызов &amp;lt;tt&amp;gt;sort()&amp;lt;/tt&amp;gt; внутри цикла на следующий фрагмент:&lt;br /&gt;
 for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
     p[i] = (p[i] - len + n) % n;&lt;br /&gt;
 countSort(p, rank);&lt;br /&gt;
&lt;br /&gt;
Здесь &amp;lt;tt&amp;gt;countSort()&amp;lt;/tt&amp;gt; — сортировка подсчётом:&lt;br /&gt;
 void countSort(vector&amp;lt;int&amp;gt; &amp;amp;p, const vector&amp;lt;int&amp;gt; &amp;amp;rank) {&lt;br /&gt;
     int n = p.size();&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;int&amp;gt; rankCount(n);&lt;br /&gt;
     for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
         rankCount[rank[i]]++;&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;int&amp;gt; rankFrom(n);&lt;br /&gt;
     for (int i = 1; i &amp;lt; n; i++)&lt;br /&gt;
         rankFrom[i] = rankFrom[i - 1] + rankCount[i - 1];&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;int&amp;gt; sortedP(n);&lt;br /&gt;
     for (int elem : p)&lt;br /&gt;
         sortedP[rankFrom[rank[elem]]++] = elem;&lt;br /&gt;
     p.swap(sortedP);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Поиск подстрок ==&lt;br /&gt;
&lt;br /&gt;
 int lowerBound(const string &amp;amp;s, const vector&amp;lt;int&amp;gt; &amp;amp;p, const string &amp;amp;t) {&lt;br /&gt;
     int l = 0, r = p.size();&lt;br /&gt;
     while (l + 1 &amp;lt; r) {&lt;br /&gt;
         int m = l + (r - l) / 2;&lt;br /&gt;
         if (s.compare(p[m], t.size(), t) &amp;gt;= 0)&lt;br /&gt;
             r = m;&lt;br /&gt;
         else&lt;br /&gt;
             l = m;&lt;br /&gt;
     }&lt;br /&gt;
     return r;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 int upperBound(const string &amp;amp;s, const vector&amp;lt;int&amp;gt; &amp;amp;p, const string &amp;amp;t) {&lt;br /&gt;
     int l = 0, r = p.size();&lt;br /&gt;
     while (l + 1 &amp;lt; r) {&lt;br /&gt;
         int m = l + (r - l) / 2;&lt;br /&gt;
         if (s.compare(p[m], t.size(), t) &amp;gt; 0)&lt;br /&gt;
             r = m;&lt;br /&gt;
         else&lt;br /&gt;
             l = m;&lt;br /&gt;
     }&lt;br /&gt;
     return r;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 int count(const string &amp;amp;s, const vector&amp;lt;int&amp;gt; &amp;amp;p, const string &amp;amp;t) {&lt;br /&gt;
     return upperBound(s, p, t) - lowerBound(s, p, t);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Построение массива LCP ==&lt;br /&gt;
&lt;br /&gt;
 vector&amp;lt;int&amp;gt; makeLCP(const string &amp;amp;s, const vector&amp;lt;int&amp;gt; &amp;amp;p) {&lt;br /&gt;
     int n = p.size();&lt;br /&gt;
     vector&amp;lt;int&amp;gt; positionInP(n);&lt;br /&gt;
     for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
         positionInP[p[i]] = i;&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;int&amp;gt; lcp(n);&lt;br /&gt;
     for (int i = 0, len = 0; i &amp;lt; n - 1; i++, len = max(len - 1, 0)) {&lt;br /&gt;
         int j = p[positionInP[i] - 1];&lt;br /&gt;
         while (s[i + len] == s[j + len])&lt;br /&gt;
             len++;&lt;br /&gt;
         lcp[positionInP[i]] = len;&lt;br /&gt;
     }&lt;br /&gt;
     return lcp;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Количество различных подстрок ==&lt;br /&gt;
&lt;br /&gt;
 long long distinctSubstringsCount(const vector&amp;lt;int&amp;gt; &amp;amp;p, const vector&amp;lt;int&amp;gt; &amp;amp;lcp) {&lt;br /&gt;
     long long res = 0;&lt;br /&gt;
     for (int i = 1; i &amp;lt; p.size(); i++)&lt;br /&gt;
         res += (p.size() - 1 - p[i]) - lcp[i];&lt;br /&gt;
     return res;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Наибольшая общая подстрока ==&lt;br /&gt;
 string longestCommonSubstring(const string &amp;amp;a, const string &amp;amp;b) {&lt;br /&gt;
     string s = a + &amp;quot;#&amp;quot; + b;&lt;br /&gt;
     auto p = makeSuffixArray(s);&lt;br /&gt;
     auto lcp = makeLCP(s, p);&lt;br /&gt;
 &lt;br /&gt;
     int len = 0, from = 0;&lt;br /&gt;
     for (int i = 1; i &amp;lt; p.size(); i++) {&lt;br /&gt;
         if ((p[i] &amp;lt; a.size() &amp;amp;&amp;amp; p[i - 1] &amp;gt; a.size() ||&lt;br /&gt;
              p[i] &amp;gt; a.size() &amp;amp;&amp;amp; p[i - 1] &amp;lt; a.size()) &amp;amp;&amp;amp; lcp[i] &amp;gt; len) {&lt;br /&gt;
             len = lcp[i];&lt;br /&gt;
             from = min(p[i], p[i - 1]);&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
     return a.substr(from, len);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Класс SuffixArray ==&lt;br /&gt;
&lt;br /&gt;
 class SuffixArray {&lt;br /&gt;
     string s;&lt;br /&gt;
     vector&amp;lt;int&amp;gt; order;&lt;br /&gt;
 &lt;br /&gt;
     void radixSort(vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; &amp;amp;rankPair) {&lt;br /&gt;
         for (int j : { 1, 0 }) {&lt;br /&gt;
             vector&amp;lt;int&amp;gt; count(rankPair.size());&lt;br /&gt;
             for (int i = 0; i &amp;lt; rankPair.size(); i++) {&lt;br /&gt;
                 int rank = rankPair[i][j];&lt;br /&gt;
                 count[rank]++;&lt;br /&gt;
             }&lt;br /&gt;
 &lt;br /&gt;
             vector&amp;lt;int&amp;gt; from(rankPair.size());&lt;br /&gt;
             for (int rank = 1; rank &amp;lt; rankPair.size(); rank++)&lt;br /&gt;
                 from[rank] = from[rank - 1] + count[rank - 1];&lt;br /&gt;
 &lt;br /&gt;
             vector&amp;lt;int&amp;gt; sortedOrder(order.size());&lt;br /&gt;
             for (int i : order) {&lt;br /&gt;
                 int rank = rankPair[i][j];&lt;br /&gt;
                 sortedOrder[from[rank]] = i;&lt;br /&gt;
                 from[rank]++;&lt;br /&gt;
             }&lt;br /&gt;
             order.swap(sortedOrder);&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     int lowerBound(const string &amp;amp;word) {&lt;br /&gt;
         int l = 0, r = order.size();&lt;br /&gt;
         while (l + 1 &amp;lt; r) {&lt;br /&gt;
             int m = l + (r - l) / 2;&lt;br /&gt;
             if (s.compare(order[m], word.size(), word) &amp;gt;= 0)&lt;br /&gt;
                 r = m;&lt;br /&gt;
             else&lt;br /&gt;
                 l = m;&lt;br /&gt;
         }&lt;br /&gt;
         return r;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     int upperBound(const string &amp;amp;word) {&lt;br /&gt;
         int l = 0, r = order.size();&lt;br /&gt;
         while (l + 1 &amp;lt; r) {&lt;br /&gt;
             int m = l + (r - l) / 2;&lt;br /&gt;
             if (s.compare(order[m], word.size(), word) &amp;gt; 0)&lt;br /&gt;
                 r = m;&lt;br /&gt;
             else&lt;br /&gt;
                 l = m;&lt;br /&gt;
         }&lt;br /&gt;
         return r;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
 public:&lt;br /&gt;
     SuffixArray(const string &amp;amp;text) : s(text + &#039;\0&#039;) {&lt;br /&gt;
         order.resize(s.size());&lt;br /&gt;
         for (int i = 0; i &amp;lt; order.size(); i++)&lt;br /&gt;
             order[i] = i;&lt;br /&gt;
         sort(order.begin(), order.end(), [&amp;amp;](int l, int r) {&lt;br /&gt;
             return s[l] &amp;lt; s[r];&lt;br /&gt;
         });&lt;br /&gt;
 &lt;br /&gt;
         vector&amp;lt;int&amp;gt; rank(s.size());&lt;br /&gt;
         rank[order[0]] = 0;&lt;br /&gt;
         for (int i = 1; i &amp;lt; s.size(); i++)&lt;br /&gt;
             rank[order[i]] = rank[order[i - 1]] + (s[order[i - 1]] != s[order[i]]);&lt;br /&gt;
 &lt;br /&gt;
         for (int halfLen = 1; halfLen &amp;lt; s.size(); halfLen *= 2) {&lt;br /&gt;
             vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; rankPair(s.size());&lt;br /&gt;
             for (int i = 0; i &amp;lt; s.size(); i++)&lt;br /&gt;
                 rankPair[i] = { rank[i], rank[(i + halfLen) % s.size()] };&lt;br /&gt;
 &lt;br /&gt;
             radixSort(rankPair);&lt;br /&gt;
 &lt;br /&gt;
             rank[order[0]] = 0;&lt;br /&gt;
             for (int i = 1; i &amp;lt; s.size(); i++)&lt;br /&gt;
                 rank[order[i]] = rank[order[i - 1]] + (rankPair[order[i - 1]] != rankPair[order[i]]);&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     int count(const string &amp;amp;word) {&lt;br /&gt;
         return upperBound(word) - lowerBound(word);&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
* [http://codeforces.com/edu/course/2/lesson/2 Codeforces EDU — Суффиксный массив]&lt;br /&gt;
* [http://e-maxx.ru/algo/suffix_array e-maxx — Суффиксный массив]&lt;br /&gt;
* [http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D1%83%D1%84%D1%84%D0%B8%D0%BA%D1%81%D0%BD%D1%8B%D0%B9_%D0%BC%D0%B0%D1%81%D1%81%D0%B8%D0%B2 neerc.info.ru/wiki — Суффиксный массив]&lt;br /&gt;
* [http://algorithmica.org/ru/suffix-array Algorithmica.org — Суффиксный массив]&lt;br /&gt;
* [http://um-nik.github.io/suffix-array Данилюк А. Суффиксный массив]&lt;br /&gt;
* [http://opentrains.mipt.ru/zksh/files/zksh2015/lectures/Suffix_Array_lecture_A.pdf Тихомиров М., Останин А. Лекция по суффиксному массиву]&lt;br /&gt;
* [http://habr.com/ru/post/115346/ Habr — Суффиксный массив — удобная замена суффиксного дерева]&lt;br /&gt;
* [http://visualgo.net/en/suffixarray VisuAlgo — Suffix Array]&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=Heavy-light-%D0%B4%D0%B5%D0%BA%D0%BE%D0%BC%D0%BF%D0%BE%D0%B7%D0%B8%D1%86%D0%B8%D1%8F&amp;diff=2948</id>
		<title>Heavy-light-декомпозиция</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=Heavy-light-%D0%B4%D0%B5%D0%BA%D0%BE%D0%BC%D0%BF%D0%BE%D0%B7%D0%B8%D1%86%D0%B8%D1%8F&amp;diff=2948"/>
		<updated>2026-02-15T00:43:22Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{|width=100%&lt;br /&gt;
|&lt;br /&gt;
Отдельное дерево отрезков для каждого пути&lt;br /&gt;
|&lt;br /&gt;
Изменение порядка рёбер в списках смежности, одно общее дерево отрезков для всех путей&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
 struct SegmentTree {&lt;br /&gt;
     int size;&lt;br /&gt;
     vector&amp;lt;int&amp;gt; t;&lt;br /&gt;
 &lt;br /&gt;
     void build(int v, int vl, int vr, vector&amp;lt;int&amp;gt; &amp;amp;path, vector&amp;lt;int&amp;gt; &amp;amp;values) {&lt;br /&gt;
         if (vl == vr) {&lt;br /&gt;
             t[v] = values[path[vl]];&lt;br /&gt;
             return;&lt;br /&gt;
         }&lt;br /&gt;
         int vm = vl + (vr - vl) / 2;&lt;br /&gt;
         build(2 * v + 1, vl, vm, path, values);&lt;br /&gt;
         build(2 * v + 2, vm + 1, vr, path, values);&lt;br /&gt;
         t[v] = max(t[2 * v + 1], t[2 * v + 2]);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     int query(int v, int vl, int vr, int l, int r) {&lt;br /&gt;
         if (l &amp;lt;= vl &amp;amp;&amp;amp; vr &amp;lt;= r)&lt;br /&gt;
             return t[v];&lt;br /&gt;
         int vm = vl + (vr - vl) / 2;&lt;br /&gt;
         if (r &amp;lt;= vm)&lt;br /&gt;
             return query(2 * v + 1, vl, vm, l, r);&lt;br /&gt;
         if (vm + 1 &amp;lt;= l)&lt;br /&gt;
             return query(2 * v + 2, vm + 1, vr, l, r);&lt;br /&gt;
         int ql = query(2 * v + 1, vl, vm, l, r);&lt;br /&gt;
         int qr = query(2 * v + 2, vm + 1, vr, l, r);&lt;br /&gt;
         return max(ql, qr);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     void modify(int v, int vl, int vr, int index, int value) {&lt;br /&gt;
         if (vl == vr) {&lt;br /&gt;
             t[v] = value;&lt;br /&gt;
             return;&lt;br /&gt;
         }&lt;br /&gt;
         int vm = vl + (vr - vl) / 2;&lt;br /&gt;
         if (index &amp;lt;= vm)&lt;br /&gt;
             modify(2 * v + 1, vl, vm, index, value);&lt;br /&gt;
         else&lt;br /&gt;
             modify(2 * v + 2, vm + 1, vr, index, value);&lt;br /&gt;
         t[v] = max(t[2 * v + 1], t[2 * v + 2]);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     SegmentTree() {}&lt;br /&gt;
 &lt;br /&gt;
     SegmentTree(vector&amp;lt;int&amp;gt; &amp;amp;path, vector&amp;lt;int&amp;gt; &amp;amp;values) : size(path.size()), t(4 * size) {&lt;br /&gt;
         build(0, 0, size - 1, path, values);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     int getMax(int l, int r) {&lt;br /&gt;
         return query(0, 0, size - 1, l, r);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     void setValue(int index, int value) {&lt;br /&gt;
         modify(0, 0, size - 1, index, value);&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
 struct HeavyLightDecomposition {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; graph, paths;&lt;br /&gt;
     vector&amp;lt;int&amp;gt; parent, depth, subtreeSize, pathOf, posOf;&lt;br /&gt;
     vector&amp;lt;SegmentTree&amp;gt; segmentTrees;&lt;br /&gt;
 &lt;br /&gt;
     HeavyLightDecomposition(int vertexCount) :&lt;br /&gt;
         graph(vertexCount), parent(vertexCount), depth(vertexCount),&lt;br /&gt;
         subtreeSize(vertexCount), pathOf(vertexCount), posOf(vertexCount) {}&lt;br /&gt;
 &lt;br /&gt;
     void addEdge(int a, int b) {&lt;br /&gt;
         graph[a].push_back(b);&lt;br /&gt;
         graph[b].push_back(a);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     int dfs1(int v, int p) {&lt;br /&gt;
         parent[v] = p;&lt;br /&gt;
         depth[v] = parent[v] != -1 ? depth[parent[v]] + 1 : 0;&lt;br /&gt;
         subtreeSize[v] = 1;&lt;br /&gt;
         for (int to : graph[v])&lt;br /&gt;
             if (to != parent[v])&lt;br /&gt;
                 subtreeSize[v] += dfs1(to, v);&lt;br /&gt;
         return subtreeSize[v];&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     void dfs2(int v, int pathIndex) {&lt;br /&gt;
         if (paths.size() == pathIndex)&lt;br /&gt;
             paths.emplace_back();&lt;br /&gt;
         paths[pathIndex].push_back(v);&lt;br /&gt;
 &lt;br /&gt;
         pathOf[v] = pathIndex;&lt;br /&gt;
         posOf[v] = paths[pathIndex].size() - 1;&lt;br /&gt;
 &lt;br /&gt;
         int maxTo = -1;&lt;br /&gt;
         for (int to : graph[v])&lt;br /&gt;
             if (to != parent[v] &amp;amp;&amp;amp; (maxTo == -1 || subtreeSize[maxTo] &amp;lt; subtreeSize[to]))&lt;br /&gt;
                 maxTo = to;&lt;br /&gt;
 &lt;br /&gt;
         for (int to : graph[v])&lt;br /&gt;
             if (to != parent[v])&lt;br /&gt;
                 dfs2(to, to == maxTo ? pathIndex : paths.size());&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     void prepare(int root, vector&amp;lt;int&amp;gt; &amp;amp;values) {&lt;br /&gt;
         dfs1(root, -1);&lt;br /&gt;
         dfs2(root, 0);&lt;br /&gt;
         for (vector&amp;lt;int&amp;gt; &amp;amp;path : paths)&lt;br /&gt;
             segmentTrees.push_back(SegmentTree(path, values));&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     int getMax(int a, int b) {&lt;br /&gt;
         int res = -1e9;&lt;br /&gt;
 &lt;br /&gt;
         while (pathOf[a] != pathOf[b]) {&lt;br /&gt;
             if (depth[paths[pathOf[a]][0]] &amp;gt; depth[paths[pathOf[b]][0]])&lt;br /&gt;
                 swap(a, b);&lt;br /&gt;
             res = max(res, segmentTrees[pathOf[b]].getMax(0, posOf[b]));&lt;br /&gt;
             b = parent[paths[pathOf[b]][0]];&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         if (depth[a] &amp;gt; depth[b])&lt;br /&gt;
             swap(a, b);&lt;br /&gt;
         res = max(res, segmentTrees[pathOf[a]].getMax(posOf[a], posOf[b]));&lt;br /&gt;
 &lt;br /&gt;
         return res;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     void setValue(int v, int value) {&lt;br /&gt;
         segmentTrees[pathOf[v]].setValue(posOf[v], value);&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
|&lt;br /&gt;
 struct SegmentTree {&lt;br /&gt;
     int size;&lt;br /&gt;
     vector&amp;lt;int&amp;gt; t;&lt;br /&gt;
 &lt;br /&gt;
     void build(int v, int vl, int vr, vector&amp;lt;int&amp;gt; &amp;amp;posInv, vector&amp;lt;int&amp;gt; &amp;amp;values) {&lt;br /&gt;
         if (vl == vr) {&lt;br /&gt;
             t[v] = values[posInv[vl]];&lt;br /&gt;
             return;&lt;br /&gt;
         }&lt;br /&gt;
         int vm = vl + (vr - vl) / 2;&lt;br /&gt;
         build(2 * v + 1, vl, vm, posInv, values);&lt;br /&gt;
         build(2 * v + 2, vm + 1, vr, posInv, values);&lt;br /&gt;
         t[v] = max(t[2 * v + 1], t[2 * v + 2]);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     int query(int v, int vl, int vr, int l, int r) {&lt;br /&gt;
         if (l &amp;lt;= vl &amp;amp;&amp;amp; vr &amp;lt;= r)&lt;br /&gt;
             return t[v];&lt;br /&gt;
         int vm = vl + (vr - vl) / 2;&lt;br /&gt;
         if (r &amp;lt;= vm)&lt;br /&gt;
             return query(2 * v + 1, vl, vm, l, r);&lt;br /&gt;
         if (vm + 1 &amp;lt;= l)&lt;br /&gt;
             return query(2 * v + 2, vm + 1, vr, l, r);&lt;br /&gt;
         int ql = query(2 * v + 1, vl, vm, l, r);&lt;br /&gt;
         int qr = query(2 * v + 2, vm + 1, vr, l, r);&lt;br /&gt;
         return max(ql, qr);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     void modify(int v, int vl, int vr, int index, int value) {&lt;br /&gt;
         if (vl == vr) {&lt;br /&gt;
             t[v] = value;&lt;br /&gt;
             return;&lt;br /&gt;
         }&lt;br /&gt;
         int vm = vl + (vr - vl) / 2;&lt;br /&gt;
         if (index &amp;lt;= vm)&lt;br /&gt;
             modify(2 * v + 1, vl, vm, index, value);&lt;br /&gt;
         else&lt;br /&gt;
             modify(2 * v + 2, vm + 1, vr, index, value);&lt;br /&gt;
         t[v] = max(t[2 * v + 1], t[2 * v + 2]);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     SegmentTree() {}&lt;br /&gt;
 &lt;br /&gt;
     SegmentTree(vector&amp;lt;int&amp;gt; &amp;amp;posInv, vector&amp;lt;int&amp;gt; &amp;amp;values) : size(posInv.size()), t(4 * size) {&lt;br /&gt;
         build(0, 0, size - 1, posInv, values);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     int getMax(int l, int r) {&lt;br /&gt;
         return query(0, 0, size - 1, l, r);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     void setValue(int index, int value) {&lt;br /&gt;
         modify(0, 0, size - 1, index, value);&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
 struct HeavyLightDecomposition {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; graph;&lt;br /&gt;
     vector&amp;lt;int&amp;gt; parent, depth, subtreeSize, pos, posInv, pathStart;&lt;br /&gt;
     int timer = 0;&lt;br /&gt;
     SegmentTree segmentTree;&lt;br /&gt;
 &lt;br /&gt;
     HeavyLightDecomposition(int vertexCount) :&lt;br /&gt;
         graph(vertexCount), parent(vertexCount), depth(vertexCount),&lt;br /&gt;
         subtreeSize(vertexCount), pos(vertexCount), posInv(vertexCount), pathStart(vertexCount) {}&lt;br /&gt;
 &lt;br /&gt;
     void addEdge(int a, int b) {&lt;br /&gt;
         graph[a].push_back(b);&lt;br /&gt;
         graph[b].push_back(a);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     int dfs1(int v, int p) {&lt;br /&gt;
         parent[v] = p;&lt;br /&gt;
         if (parent[v] != -1) {&lt;br /&gt;
             depth[v] = depth[parent[v]] + 1;&lt;br /&gt;
             graph[v].erase(find(graph[v].begin(), graph[v].end(), parent[v]));&lt;br /&gt;
         }&lt;br /&gt;
         subtreeSize[v] = 1;&lt;br /&gt;
         for (int &amp;amp;to : graph[v]) {&lt;br /&gt;
             subtreeSize[v] += dfs1(to, v);&lt;br /&gt;
             if (subtreeSize[graph[v][0]] &amp;lt; subtreeSize[to])&lt;br /&gt;
                 swap(graph[v][0], to);&lt;br /&gt;
         }&lt;br /&gt;
         return subtreeSize[v];&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     void dfs2(int v, int start) {&lt;br /&gt;
         pos[v] = timer;&lt;br /&gt;
         posInv[timer++] = v;&lt;br /&gt;
         pathStart[v] = start;&lt;br /&gt;
         for (int to : graph[v])&lt;br /&gt;
             dfs2(to, to == graph[v][0] ? pathStart[v] : to);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     void prepare(int root, vector&amp;lt;int&amp;gt; &amp;amp;values) {&lt;br /&gt;
         dfs1(root, -1);&lt;br /&gt;
         dfs2(root, root);&lt;br /&gt;
         segmentTree = SegmentTree(posInv, values);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     int getMax(int a, int b) {&lt;br /&gt;
         int res = -1e9;&lt;br /&gt;
 &lt;br /&gt;
         while (pathStart[a] != pathStart[b]) {&lt;br /&gt;
             if (depth[pathStart[a]] &amp;gt; depth[pathStart[b]])&lt;br /&gt;
                 swap(a, b);&lt;br /&gt;
             res = max(res, segmentTree.getMax(pos[pathStart[b]], pos[b]));&lt;br /&gt;
             b = parent[pathStart[b]];&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         if (depth[a] &amp;gt; depth[b])&lt;br /&gt;
             swap(a, b);&lt;br /&gt;
         res = max(res, segmentTree.getMax(pos[a], pos[b]));&lt;br /&gt;
 &lt;br /&gt;
         return res;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     void setValue(int v, int value) {&lt;br /&gt;
         segmentTree.setValue(pos[v], value);&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Ссылки на задачи ==&lt;br /&gt;
* [http://acm.timus.ru/problem.aspx?num=1553 Timus #1553 &amp;amp;mdash; Caves and Tunnels]&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
Теория:&lt;br /&gt;
* [https://e-maxx.ru/algo/heavy_light e-maxx.ru — Heavy-light декомпозиция]&lt;br /&gt;
* [https://cp-algorithms.com/graph/hld.html cp-algorithms.com — Heavy-light decomposition]&lt;br /&gt;
* [https://algorithmica.org/ru/hld algorithmica.org — Heavy-light декомпозиция]&lt;br /&gt;
* [https://codeforces.com/blog/entry/12239 codeforces.com — Heavy-light decompositon — это может быть просто!]&lt;br /&gt;
* [https://codeforces.com/blog/entry/81317 codeforces.com — Hybrid Tutorial #-1: Heavy-Light Decomposition]&lt;br /&gt;
* [https://web.archive.org/web/20221128105153/https://blog.anudeep2011.com/heavy-light-decomposition/ blog.anudeep2011.com — Heavy Light Decomposition]&lt;br /&gt;
* [https://usaco.guide/plat/hld usaco.guide — Heavy-Light Decomposition]&lt;br /&gt;
Код:&lt;br /&gt;
* [https://github.com/indy256/codelibrary/blob/main/cpp/structures/heavy_light_decomposition.cpp codelibrary/cpp/structures/heavy_light_decomposition.cpp]&lt;br /&gt;
* [https://github.com/ADJA/algos/blob/master/Graphs/HLD.cpp algos/Graphs/HLD.cpp]&lt;br /&gt;
* [https://github.com/ADJA/algos/blob/master/Graphs/LCAHLD.cpp algos/Graphs/LCAHLD.cpp]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Учебный курс «Алгоритмы и структуры данных»]]&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=Heavy-light-%D0%B4%D0%B5%D0%BA%D0%BE%D0%BC%D0%BF%D0%BE%D0%B7%D0%B8%D1%86%D0%B8%D1%8F&amp;diff=2947</id>
		<title>Heavy-light-декомпозиция</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=Heavy-light-%D0%B4%D0%B5%D0%BA%D0%BE%D0%BC%D0%BF%D0%BE%D0%B7%D0%B8%D1%86%D0%B8%D1%8F&amp;diff=2947"/>
		<updated>2026-02-14T22:35:34Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt; struct SegmentTree {&lt;br /&gt;
     int size;&lt;br /&gt;
     vector&amp;lt;int&amp;gt; t;&lt;br /&gt;
 &lt;br /&gt;
     int query(int v, int vl, int vr, int l, int r) {&lt;br /&gt;
         if (vr &amp;lt; l || r &amp;lt; vl)&lt;br /&gt;
             return -1e9;&lt;br /&gt;
         if (l &amp;lt;= vl &amp;amp;&amp;amp; vr &amp;lt;= r)&lt;br /&gt;
             return t[v];&lt;br /&gt;
         int vm = vl + (vr - vl) / 2;&lt;br /&gt;
         int ql = query(2 * v + 1, vl, vm, l, r);&lt;br /&gt;
         int qr = query(2 * v + 2, vm + 1, vr, l, r);&lt;br /&gt;
         return max(ql, qr);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     void modify(int v, int vl, int vr, int index, int value) {&lt;br /&gt;
         if (vr &amp;lt; index || index &amp;lt; vl)&lt;br /&gt;
             return;&lt;br /&gt;
         if (vl == vr) {&lt;br /&gt;
             t[v] += value;&lt;br /&gt;
             return;&lt;br /&gt;
         }&lt;br /&gt;
         int vm = vl + (vr - vl) / 2;&lt;br /&gt;
         modify(2 * v + 1, vl, vm, index, value);&lt;br /&gt;
         modify(2 * v + 2, vm + 1, vr, index, value);&lt;br /&gt;
         t[v] = max(t[2 * v + 1], t[2 * v + 2]);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     SegmentTree(int size) : size(size), t(4 * size) {}&lt;br /&gt;
 &lt;br /&gt;
     int getMax(int l, int r) {&lt;br /&gt;
         return query(0, 0, size - 1, l, r);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     void addValue(int index, int value) {&lt;br /&gt;
         modify(0, 0, size - 1, index, value);&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
 struct HeavyLightDecomposition {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; graph;&lt;br /&gt;
     vector&amp;lt;int&amp;gt; l, r, parent, subtreeSize;&lt;br /&gt;
     int timer = 0;&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; paths;&lt;br /&gt;
     vector&amp;lt;int&amp;gt; pathOf, posOf;&lt;br /&gt;
     vector&amp;lt;SegmentTree&amp;gt; segmentTrees;&lt;br /&gt;
 &lt;br /&gt;
     HeavyLightDecomposition(int vertexCount) :&lt;br /&gt;
         graph(vertexCount), l(vertexCount), r(vertexCount),&lt;br /&gt;
         parent(vertexCount), subtreeSize(vertexCount),&lt;br /&gt;
         pathOf(vertexCount), posOf(vertexCount) {}&lt;br /&gt;
 &lt;br /&gt;
     void addEdge(int a, int b) {&lt;br /&gt;
         graph[a].push_back(b);&lt;br /&gt;
         graph[b].push_back(a);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     int dfs(int v, int p) {&lt;br /&gt;
         l[v] = timer++;&lt;br /&gt;
         parent[v] = p;&lt;br /&gt;
         subtreeSize[v] = 1;&lt;br /&gt;
 &lt;br /&gt;
         for (int to : graph[v])&lt;br /&gt;
             if (to != parent[v])&lt;br /&gt;
                 subtreeSize[v] += dfs(to, v);&lt;br /&gt;
 &lt;br /&gt;
         r[v] = timer++;&lt;br /&gt;
         return subtreeSize[v];&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     bool isAncestor(int a, int b) {&lt;br /&gt;
         return l[a] &amp;lt;= l[b] &amp;amp;&amp;amp; r[b] &amp;lt;= r[a];&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     void buildPath(int v, int pathIndex) {&lt;br /&gt;
         if (paths.size() == pathIndex)&lt;br /&gt;
             paths.emplace_back();&lt;br /&gt;
         paths[pathIndex].push_back(v);&lt;br /&gt;
 &lt;br /&gt;
         pathOf[v] = pathIndex;&lt;br /&gt;
         posOf[v] = paths[pathIndex].size() - 1;&lt;br /&gt;
 &lt;br /&gt;
         int maxTo = -1;&lt;br /&gt;
         for (int to : graph[v])&lt;br /&gt;
             if (to != parent[v] &amp;amp;&amp;amp; (maxTo == -1 || subtreeSize[maxTo] &amp;lt; subtreeSize[to]))&lt;br /&gt;
                 maxTo = to;&lt;br /&gt;
 &lt;br /&gt;
         for (int to : graph[v])&lt;br /&gt;
             if (to != parent[v])&lt;br /&gt;
                 buildPath(to, to == maxTo ? pathIndex : paths.size());&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     void prepare(int root) {&lt;br /&gt;
         dfs(root, -1);&lt;br /&gt;
         buildPath(root, 0);&lt;br /&gt;
         for (vector&amp;lt;int&amp;gt; &amp;amp;path : paths)&lt;br /&gt;
             segmentTrees.push_back(SegmentTree(path.size()));&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     int getMax(int a, int b) {&lt;br /&gt;
         int res = 0;&lt;br /&gt;
 &lt;br /&gt;
         for (; !isAncestor(paths[pathOf[a]][0], b); a = parent[paths[pathOf[a]][0]])&lt;br /&gt;
             res = max(res, segmentTrees[pathOf[a]].getMax(0, posOf[a]));&lt;br /&gt;
         for (; !isAncestor(paths[pathOf[b]][0], a); b = parent[paths[pathOf[b]][0]])&lt;br /&gt;
             res = max(res, segmentTrees[pathOf[b]].getMax(0, posOf[b]));&lt;br /&gt;
 &lt;br /&gt;
         if (posOf[a] &amp;gt; posOf[b])&lt;br /&gt;
             swap(a, b);&lt;br /&gt;
         return max(res, segmentTrees[pathOf[a]].getMax(posOf[a], posOf[b]));&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     void addValue(int v, int value) {&lt;br /&gt;
         segmentTrees[pathOf[v]].addValue(posOf[v], value);&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
== Ссылки на задачи ==&lt;br /&gt;
* [http://acm.timus.ru/problem.aspx?num=1553 Timus #1553 &amp;amp;mdash; Caves and Tunnels]&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
Теория:&lt;br /&gt;
* [https://e-maxx.ru/algo/heavy_light e-maxx.ru — Heavy-light декомпозиция]&lt;br /&gt;
* [https://cp-algorithms.com/graph/hld.html cp-algorithms.com — Heavy-light decomposition]&lt;br /&gt;
* [https://algorithmica.org/ru/hld algorithmica.org — Heavy-light декомпозиция]&lt;br /&gt;
* [https://codeforces.com/blog/entry/12239 codeforces.com — Heavy-light decompositon — это может быть просто!]&lt;br /&gt;
* [https://codeforces.com/blog/entry/81317 codeforces.com — Hybrid Tutorial #-1: Heavy-Light Decomposition]&lt;br /&gt;
* [https://web.archive.org/web/20221128105153/https://blog.anudeep2011.com/heavy-light-decomposition/ blog.anudeep2011.com — Heavy Light Decomposition]&lt;br /&gt;
* [https://usaco.guide/plat/hld usaco.guide — Heavy-Light Decomposition]&lt;br /&gt;
Код:&lt;br /&gt;
* [https://github.com/indy256/codelibrary/blob/main/cpp/structures/heavy_light_decomposition.cpp codelibrary/cpp/structures/heavy_light_decomposition.cpp]&lt;br /&gt;
* [https://github.com/ADJA/algos/blob/master/Graphs/HLD.cpp algos/Graphs/HLD.cpp]&lt;br /&gt;
* [https://github.com/ADJA/algos/blob/master/Graphs/LCAHLD.cpp algos/Graphs/LCAHLD.cpp]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Учебный курс «Алгоритмы и структуры данных»]]&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=Heavy-light-%D0%B4%D0%B5%D0%BA%D0%BE%D0%BC%D0%BF%D0%BE%D0%B7%D0%B8%D1%86%D0%B8%D1%8F&amp;diff=2946</id>
		<title>Heavy-light-декомпозиция</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=Heavy-light-%D0%B4%D0%B5%D0%BA%D0%BE%D0%BC%D0%BF%D0%BE%D0%B7%D0%B8%D1%86%D0%B8%D1%8F&amp;diff=2946"/>
		<updated>2026-02-14T22:29:20Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: /* Ссылки */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt; struct SegmentTree {&lt;br /&gt;
     int size;&lt;br /&gt;
     vector&amp;lt;int&amp;gt; t;&lt;br /&gt;
 &lt;br /&gt;
     int query(int v, int vl, int vr, int l, int r) {&lt;br /&gt;
         if (vr &amp;lt; l || r &amp;lt; vl)&lt;br /&gt;
             return -1e9;&lt;br /&gt;
         if (l &amp;lt;= vl &amp;amp;&amp;amp; vr &amp;lt;= r)&lt;br /&gt;
             return t[v];&lt;br /&gt;
         int vm = vl + (vr - vl) / 2;&lt;br /&gt;
         int ql = query(2 * v + 1, vl, vm, l, r);&lt;br /&gt;
         int qr = query(2 * v + 2, vm + 1, vr, l, r);&lt;br /&gt;
         return max(ql, qr);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     void modify(int v, int vl, int vr, int index, int value) {&lt;br /&gt;
         if (vr &amp;lt; index || index &amp;lt; vl)&lt;br /&gt;
             return;&lt;br /&gt;
         if (vl == vr) {&lt;br /&gt;
             t[v] += value;&lt;br /&gt;
             return;&lt;br /&gt;
         }&lt;br /&gt;
         int vm = vl + (vr - vl) / 2;&lt;br /&gt;
         modify(2 * v + 1, vl, vm, index, value);&lt;br /&gt;
         modify(2 * v + 2, vm + 1, vr, index, value);&lt;br /&gt;
         t[v] = max(t[2 * v + 1], t[2 * v + 2]);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     SegmentTree(int size) : size(size), t(4 * size) {}&lt;br /&gt;
 &lt;br /&gt;
     int getMax(int l, int r) {&lt;br /&gt;
         return query(0, 0, size - 1, l, r);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     void addValue(int index, int value) {&lt;br /&gt;
         modify(0, 0, size - 1, index, value);&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
 struct HeavyLightDecomposition {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; graph;&lt;br /&gt;
     vector&amp;lt;int&amp;gt; l, r, parent, subtreeSize;&lt;br /&gt;
     int timer = 0;&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; paths;&lt;br /&gt;
     vector&amp;lt;int&amp;gt; pathOf, posOf;&lt;br /&gt;
     vector&amp;lt;SegmentTree&amp;gt; segmentTrees;&lt;br /&gt;
 &lt;br /&gt;
     HeavyLightDecomposition(int vertexCount) :&lt;br /&gt;
         graph(vertexCount), l(vertexCount), r(vertexCount),&lt;br /&gt;
         parent(vertexCount), subtreeSize(vertexCount),&lt;br /&gt;
         pathOf(vertexCount), posOf(vertexCount) {}&lt;br /&gt;
 &lt;br /&gt;
     void addEdge(int a, int b) {&lt;br /&gt;
         graph[a].push_back(b);&lt;br /&gt;
         graph[b].push_back(a);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     int dfs(int v, int p) {&lt;br /&gt;
         l[v] = timer++;&lt;br /&gt;
         parent[v] = p;&lt;br /&gt;
         subtreeSize[v] = 1;&lt;br /&gt;
 &lt;br /&gt;
         for (int to : graph[v])&lt;br /&gt;
             if (to != p)&lt;br /&gt;
                 subtreeSize[v] += dfs(to, v);&lt;br /&gt;
 &lt;br /&gt;
         r[v] = timer++;&lt;br /&gt;
         return subtreeSize[v];&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     bool isAncestor(int a, int b) {&lt;br /&gt;
         return l[a] &amp;lt;= l[b] &amp;amp;&amp;amp; r[b] &amp;lt;= r[a];&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     void buildPath(int v, int pathIndex) {&lt;br /&gt;
         if (paths.size() == pathIndex)&lt;br /&gt;
             paths.emplace_back();&lt;br /&gt;
         paths[pathIndex].push_back(v);&lt;br /&gt;
 &lt;br /&gt;
         pathOf[v] = pathIndex;&lt;br /&gt;
         posOf[v] = paths[pathIndex].size() - 1;&lt;br /&gt;
 &lt;br /&gt;
         int maxTo = -1;&lt;br /&gt;
         for (int to : graph[v])&lt;br /&gt;
             if (to != parent[v] &amp;amp;&amp;amp; (maxTo == -1 || subtreeSize[maxTo] &amp;lt; subtreeSize[to]))&lt;br /&gt;
                 maxTo = to;&lt;br /&gt;
 &lt;br /&gt;
         for (int to : graph[v]) {&lt;br /&gt;
             if (to == parent[v])&lt;br /&gt;
                 continue;&lt;br /&gt;
             if (to == maxTo)&lt;br /&gt;
                 buildPath(to, pathIndex);&lt;br /&gt;
             else&lt;br /&gt;
                 buildPath(to, paths.size());&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     void prepare(int root) {&lt;br /&gt;
         dfs(root, -1);&lt;br /&gt;
         buildPath(root, 0);&lt;br /&gt;
         for (vector&amp;lt;int&amp;gt; &amp;amp;path : paths)&lt;br /&gt;
             segmentTrees.push_back(SegmentTree(path.size()));&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     int getMax(int a, int b) {&lt;br /&gt;
         int res = 0;&lt;br /&gt;
 &lt;br /&gt;
         for (; !isAncestor(paths[pathOf[a]][0], b); a = parent[paths[pathOf[a]][0]])&lt;br /&gt;
             res = max(res, segmentTrees[pathOf[a]].getMax(0, posOf[a]));&lt;br /&gt;
         for (; !isAncestor(paths[pathOf[b]][0], a); b = parent[paths[pathOf[b]][0]])&lt;br /&gt;
             res = max(res, segmentTrees[pathOf[b]].getMax(0, posOf[b]));&lt;br /&gt;
 &lt;br /&gt;
         if (posOf[a] &amp;gt; posOf[b])&lt;br /&gt;
             swap(a, b);&lt;br /&gt;
         return max(res, segmentTrees[pathOf[a]].getMax(posOf[a], posOf[b]));&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     void addValue(int v, int value) {&lt;br /&gt;
         segmentTrees[pathOf[v]].addValue(posOf[v], value);&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
== Ссылки на задачи ==&lt;br /&gt;
* [http://acm.timus.ru/problem.aspx?num=1553 Timus #1553 &amp;amp;mdash; Caves and Tunnels]&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
Теория:&lt;br /&gt;
* [https://e-maxx.ru/algo/heavy_light e-maxx.ru — Heavy-light декомпозиция]&lt;br /&gt;
* [https://cp-algorithms.com/graph/hld.html cp-algorithms.com — Heavy-light decomposition]&lt;br /&gt;
* [https://algorithmica.org/ru/hld algorithmica.org — Heavy-light декомпозиция]&lt;br /&gt;
* [https://codeforces.com/blog/entry/12239 codeforces.com — Heavy-light decompositon — это может быть просто!]&lt;br /&gt;
* [https://codeforces.com/blog/entry/81317 codeforces.com — Hybrid Tutorial #-1: Heavy-Light Decomposition]&lt;br /&gt;
* [https://web.archive.org/web/20221128105153/https://blog.anudeep2011.com/heavy-light-decomposition/ blog.anudeep2011.com — Heavy Light Decomposition]&lt;br /&gt;
* [https://usaco.guide/plat/hld usaco.guide — Heavy-Light Decomposition]&lt;br /&gt;
Код:&lt;br /&gt;
* [https://github.com/indy256/codelibrary/blob/main/cpp/structures/heavy_light_decomposition.cpp codelibrary/cpp/structures/heavy_light_decomposition.cpp]&lt;br /&gt;
* [https://github.com/ADJA/algos/blob/master/Graphs/HLD.cpp algos/Graphs/HLD.cpp]&lt;br /&gt;
* [https://github.com/ADJA/algos/blob/master/Graphs/LCAHLD.cpp algos/Graphs/LCAHLD.cpp]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Учебный курс «Алгоритмы и структуры данных»]]&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=%D0%9A%D0%B0%D1%82%D0%B5%D0%B3%D0%BE%D1%80%D0%B8%D1%8F:%D0%A3%D1%87%D0%B5%D0%B1%D0%BD%D1%8B%D0%B9_%D0%BA%D1%83%D1%80%D1%81_%C2%AB%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D1%8B_%D0%B8_%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D1%8B_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85%C2%BB&amp;diff=2945</id>
		<title>Категория:Учебный курс «Алгоритмы и структуры данных»</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=%D0%9A%D0%B0%D1%82%D0%B5%D0%B3%D0%BE%D1%80%D0%B8%D1%8F:%D0%A3%D1%87%D0%B5%D0%B1%D0%BD%D1%8B%D0%B9_%D0%BA%D1%83%D1%80%D1%81_%C2%AB%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D1%8B_%D0%B8_%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D1%8B_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85%C2%BB&amp;diff=2945"/>
		<updated>2026-02-14T22:01:18Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&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;
:* [[Поразрядная сортировка]]&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;
:* [[Множество. Реализация на битовых векторах]]&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;
:* 📄 [[Sparse table]]&lt;br /&gt;
:* Sqrt-декомпозиция&lt;br /&gt;
:* [[Алгоритм Мо]]&lt;br /&gt;
|width=450px|&lt;br /&gt;
;Алгоритмы для работы с графами&lt;br /&gt;
:&lt;br /&gt;
:* [[Основные определения. Представление графов]]&lt;br /&gt;
: Поиск в глубину и его приложения&lt;br /&gt;
:* 📄 [[Поиск в глубину]]&amp;lt;sup&amp;gt;&#039;&#039;O(V+E)&#039;&#039;&amp;lt;/sup&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;
:* 📄 [[Поиск в ширину]]&amp;lt;sup&amp;gt;&#039;&#039;O(V+E)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* 📝 [[Алгоритм Дейкстры]]&amp;lt;sup&amp;gt;&#039;&#039;O(V&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;+E) или O(ElogV)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* 📄 [[Алгоритм A*]]&lt;br /&gt;
:* [[Алгоритм Форда-Беллмана]]&amp;lt;sup&amp;gt;&#039;&#039;O(VE)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* [[Кратчайшие пути в ациклических орграфах]]&amp;lt;sup&amp;gt;&#039;&#039;O(V+E)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
: Кратчайшие пути между всеми парами вершин&lt;br /&gt;
:* 📄 [[Алгоритм Флойда]]&amp;lt;sup&amp;gt;&#039;&#039;O(V&amp;lt;sup&amp;gt;3&amp;lt;/sup&amp;gt;)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* [[Алгоритм Джонсона]]&amp;lt;sup&amp;gt;&#039;&#039;O(VElogV)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
: Минимальное остовное дерево&lt;br /&gt;
:* 📝 [[Алгоритм Краскала]]&amp;lt;sup&amp;gt;&#039;&#039;O(ElogV)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* 📄 [[Алгоритм Прима]]&amp;lt;sup&amp;gt;&#039;&#039;O(V&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;+E) или O(ElogV)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
: Максимальный поток&lt;br /&gt;
:* 📄 [[Алгоритм Форда-Фалкерсона]]&amp;lt;sup&amp;gt;&#039;&#039;O(Flow&amp;amp;times;E)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* 📄 [[Алгоритм Эдмондса-Карпа]]&amp;lt;sup&amp;gt;&#039;&#039;O(VE&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* 📄 [[Алгоритм Диница]]&amp;lt;sup&amp;gt;&#039;&#039;O(V&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;E)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* 📄 [[Максимальный поток минимальной стоимости]]&lt;br /&gt;
:* [[Применения максимального потока]]&lt;br /&gt;
: Максимальное паросочетание&lt;br /&gt;
:* 📄 [[Алгоритм Куна]]&amp;lt;sup&amp;gt;&#039;&#039;O(VE)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* 📄 [[Минимальное вершинное покрытие, максимальное независимое множество]]&lt;br /&gt;
: Наименьший общий предок&lt;br /&gt;
:* 📄 [[Метод двоичного подъёма]]&amp;lt;sup&amp;gt;&#039;&#039;O(NlogN), O(logN)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* [[Сведение LCA к RMQ и RMQ к LCA]]&lt;br /&gt;
:* [[Алгоритм Тарьяна (offline)]]&amp;lt;sup&amp;gt;&#039;&#039;O(N), O(1)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
: Декомпозиции деревьев&lt;br /&gt;
:* 📄 [[Heavy-light-декомпозиция]]&lt;br /&gt;
|&lt;br /&gt;
;Полный перебор и методы его оптимизации&lt;br /&gt;
:&lt;br /&gt;
:* [[Полный перебор]]&lt;br /&gt;
:* [[Два указателя]]&lt;br /&gt;
:* Meet in the middle&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;
:* 📄 [[Метод Гаусса]]&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;
:* 📄 [[Хеширование строк]]&lt;br /&gt;
:* 📄 [[Префикс-функция]]&lt;br /&gt;
:* 📄 [[Алгоритм Ахо-Корасик]]&lt;br /&gt;
:* 📄 [[Z-функция]]&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;
&#039;&#039;&amp;amp;copy; В. А. Фолунин, 2012–2026&#039;&#039;&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=%D0%9A%D0%B0%D1%82%D0%B5%D0%B3%D0%BE%D1%80%D0%B8%D1%8F:%D0%A3%D1%87%D0%B5%D0%B1%D0%BD%D1%8B%D0%B9_%D0%BA%D1%83%D1%80%D1%81_%C2%AB%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D1%8B_%D0%B8_%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D1%8B_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85%C2%BB&amp;diff=2944</id>
		<title>Категория:Учебный курс «Алгоритмы и структуры данных»</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=%D0%9A%D0%B0%D1%82%D0%B5%D0%B3%D0%BE%D1%80%D0%B8%D1%8F:%D0%A3%D1%87%D0%B5%D0%B1%D0%BD%D1%8B%D0%B9_%D0%BA%D1%83%D1%80%D1%81_%C2%AB%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D1%8B_%D0%B8_%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D1%8B_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85%C2%BB&amp;diff=2944"/>
		<updated>2026-02-14T21:59:41Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&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;
:* [[Поразрядная сортировка]]&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;
:* [[Множество. Реализация на битовых векторах]]&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;
:* 📄 [[Sparse table]]&lt;br /&gt;
:* Sqrt-декомпозиция&lt;br /&gt;
:* [[Алгоритм Мо]]&lt;br /&gt;
|width=450px|&lt;br /&gt;
;Алгоритмы для работы с графами&lt;br /&gt;
:&lt;br /&gt;
:* [[Основные определения. Представление графов]]&lt;br /&gt;
: Поиск в глубину и его приложения&lt;br /&gt;
:* 📄 [[Поиск в глубину]]&amp;lt;sup&amp;gt;&#039;&#039;O(V+E)&#039;&#039;&amp;lt;/sup&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;
:* 📄 [[Поиск в ширину]]&amp;lt;sup&amp;gt;&#039;&#039;O(V+E)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* 📝 [[Алгоритм Дейкстры]]&amp;lt;sup&amp;gt;&#039;&#039;O(V&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;+E) или O(ElogV)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* 📄 [[Алгоритм A*]]&lt;br /&gt;
:* [[Алгоритм Форда-Беллмана]]&amp;lt;sup&amp;gt;&#039;&#039;O(VE)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* [[Кратчайшие пути в ациклических орграфах]]&amp;lt;sup&amp;gt;&#039;&#039;O(V+E)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
: Кратчайшие пути между всеми парами вершин&lt;br /&gt;
:* 📄 [[Алгоритм Флойда]]&amp;lt;sup&amp;gt;&#039;&#039;O(V&amp;lt;sup&amp;gt;3&amp;lt;/sup&amp;gt;)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* [[Алгоритм Джонсона]]&amp;lt;sup&amp;gt;&#039;&#039;O(VElogV)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
: Минимальное остовное дерево&lt;br /&gt;
:* 📝 [[Алгоритм Краскала]]&amp;lt;sup&amp;gt;&#039;&#039;O(ElogV)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* 📄 [[Алгоритм Прима]]&amp;lt;sup&amp;gt;&#039;&#039;O(V&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;+E) или O(ElogV)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
: Максимальный поток&lt;br /&gt;
:* 📄 [[Алгоритм Форда-Фалкерсона]]&amp;lt;sup&amp;gt;&#039;&#039;O(Flow&amp;amp;times;E)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* 📄 [[Алгоритм Эдмондса-Карпа]]&amp;lt;sup&amp;gt;&#039;&#039;O(VE&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* 📄 [[Алгоритм Диница]]&amp;lt;sup&amp;gt;&#039;&#039;O(V&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;E)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* 📝 [[Максимальный поток минимальной стоимости]]&lt;br /&gt;
:* [[Применения максимального потока]]&lt;br /&gt;
: Максимальное паросочетание&lt;br /&gt;
:* 📄 [[Алгоритм Куна]]&amp;lt;sup&amp;gt;&#039;&#039;O(VE)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* 📄 [[Минимальное вершинное покрытие, максимальное независимое множество]]&lt;br /&gt;
: Наименьший общий предок&lt;br /&gt;
:* 📄 [[Метод двоичного подъёма]]&amp;lt;sup&amp;gt;&#039;&#039;O(NlogN), O(logN)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* [[Сведение LCA к RMQ и RMQ к LCA]]&lt;br /&gt;
:* [[Алгоритм Тарьяна (offline)]]&amp;lt;sup&amp;gt;&#039;&#039;O(N), O(1)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
: Декомпозиции деревьев&lt;br /&gt;
:* 📄 [[Heavy-light-декомпозиция]]&lt;br /&gt;
|&lt;br /&gt;
;Полный перебор и методы его оптимизации&lt;br /&gt;
:&lt;br /&gt;
:* [[Полный перебор]]&lt;br /&gt;
:* [[Два указателя]]&lt;br /&gt;
:* Meet in the middle&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;
:* 📄 [[Метод Гаусса]]&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;
:* 📄 [[Хеширование строк]]&lt;br /&gt;
:* 📄 [[Префикс-функция]]&lt;br /&gt;
:* 📄 [[Алгоритм Ахо-Корасик]]&lt;br /&gt;
:* 📄 [[Z-функция]]&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;
&#039;&#039;&amp;amp;copy; В. А. Фолунин, 2012–2026&#039;&#039;&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=Heavy-light-%D0%B4%D0%B5%D0%BA%D0%BE%D0%BC%D0%BF%D0%BE%D0%B7%D0%B8%D1%86%D0%B8%D1%8F&amp;diff=2943</id>
		<title>Heavy-light-декомпозиция</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=Heavy-light-%D0%B4%D0%B5%D0%BA%D0%BE%D0%BC%D0%BF%D0%BE%D0%B7%D0%B8%D1%86%D0%B8%D1%8F&amp;diff=2943"/>
		<updated>2026-02-14T21:58:56Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt; struct SegmentTree {&lt;br /&gt;
     int size;&lt;br /&gt;
     vector&amp;lt;int&amp;gt; t;&lt;br /&gt;
 &lt;br /&gt;
     int query(int v, int vl, int vr, int l, int r) {&lt;br /&gt;
         if (vr &amp;lt; l || r &amp;lt; vl)&lt;br /&gt;
             return -1e9;&lt;br /&gt;
         if (l &amp;lt;= vl &amp;amp;&amp;amp; vr &amp;lt;= r)&lt;br /&gt;
             return t[v];&lt;br /&gt;
         int vm = vl + (vr - vl) / 2;&lt;br /&gt;
         int ql = query(2 * v + 1, vl, vm, l, r);&lt;br /&gt;
         int qr = query(2 * v + 2, vm + 1, vr, l, r);&lt;br /&gt;
         return max(ql, qr);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     void modify(int v, int vl, int vr, int index, int value) {&lt;br /&gt;
         if (vr &amp;lt; index || index &amp;lt; vl)&lt;br /&gt;
             return;&lt;br /&gt;
         if (vl == vr) {&lt;br /&gt;
             t[v] += value;&lt;br /&gt;
             return;&lt;br /&gt;
         }&lt;br /&gt;
         int vm = vl + (vr - vl) / 2;&lt;br /&gt;
         modify(2 * v + 1, vl, vm, index, value);&lt;br /&gt;
         modify(2 * v + 2, vm + 1, vr, index, value);&lt;br /&gt;
         t[v] = max(t[2 * v + 1], t[2 * v + 2]);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     SegmentTree(int size) : size(size), t(4 * size) {}&lt;br /&gt;
 &lt;br /&gt;
     int getMax(int l, int r) {&lt;br /&gt;
         return query(0, 0, size - 1, l, r);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     void addValue(int index, int value) {&lt;br /&gt;
         modify(0, 0, size - 1, index, value);&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
 struct HeavyLightDecomposition {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; graph;&lt;br /&gt;
     vector&amp;lt;int&amp;gt; l, r, parent, subtreeSize;&lt;br /&gt;
     int timer = 0;&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; paths;&lt;br /&gt;
     vector&amp;lt;int&amp;gt; pathOf, posOf;&lt;br /&gt;
     vector&amp;lt;SegmentTree&amp;gt; segmentTrees;&lt;br /&gt;
 &lt;br /&gt;
     HeavyLightDecomposition(int vertexCount) :&lt;br /&gt;
         graph(vertexCount), l(vertexCount), r(vertexCount),&lt;br /&gt;
         parent(vertexCount), subtreeSize(vertexCount),&lt;br /&gt;
         pathOf(vertexCount), posOf(vertexCount) {}&lt;br /&gt;
 &lt;br /&gt;
     void addEdge(int a, int b) {&lt;br /&gt;
         graph[a].push_back(b);&lt;br /&gt;
         graph[b].push_back(a);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     int dfs(int v, int p) {&lt;br /&gt;
         l[v] = timer++;&lt;br /&gt;
         parent[v] = p;&lt;br /&gt;
         subtreeSize[v] = 1;&lt;br /&gt;
 &lt;br /&gt;
         for (int to : graph[v])&lt;br /&gt;
             if (to != p)&lt;br /&gt;
                 subtreeSize[v] += dfs(to, v);&lt;br /&gt;
 &lt;br /&gt;
         r[v] = timer++;&lt;br /&gt;
         return subtreeSize[v];&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     bool isAncestor(int a, int b) {&lt;br /&gt;
         return l[a] &amp;lt;= l[b] &amp;amp;&amp;amp; r[b] &amp;lt;= r[a];&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     void buildPath(int v, int pathIndex) {&lt;br /&gt;
         if (paths.size() == pathIndex)&lt;br /&gt;
             paths.emplace_back();&lt;br /&gt;
         paths[pathIndex].push_back(v);&lt;br /&gt;
 &lt;br /&gt;
         pathOf[v] = pathIndex;&lt;br /&gt;
         posOf[v] = paths[pathIndex].size() - 1;&lt;br /&gt;
 &lt;br /&gt;
         int maxTo = -1;&lt;br /&gt;
         for (int to : graph[v])&lt;br /&gt;
             if (to != parent[v] &amp;amp;&amp;amp; (maxTo == -1 || subtreeSize[maxTo] &amp;lt; subtreeSize[to]))&lt;br /&gt;
                 maxTo = to;&lt;br /&gt;
 &lt;br /&gt;
         for (int to : graph[v]) {&lt;br /&gt;
             if (to == parent[v])&lt;br /&gt;
                 continue;&lt;br /&gt;
             if (to == maxTo)&lt;br /&gt;
                 buildPath(to, pathIndex);&lt;br /&gt;
             else&lt;br /&gt;
                 buildPath(to, paths.size());&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     void prepare(int root) {&lt;br /&gt;
         dfs(root, -1);&lt;br /&gt;
         buildPath(root, 0);&lt;br /&gt;
         for (vector&amp;lt;int&amp;gt; &amp;amp;path : paths)&lt;br /&gt;
             segmentTrees.push_back(SegmentTree(path.size()));&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     int getMax(int a, int b) {&lt;br /&gt;
         int res = 0;&lt;br /&gt;
 &lt;br /&gt;
         for (; !isAncestor(paths[pathOf[a]][0], b); a = parent[paths[pathOf[a]][0]])&lt;br /&gt;
             res = max(res, segmentTrees[pathOf[a]].getMax(0, posOf[a]));&lt;br /&gt;
         for (; !isAncestor(paths[pathOf[b]][0], a); b = parent[paths[pathOf[b]][0]])&lt;br /&gt;
             res = max(res, segmentTrees[pathOf[b]].getMax(0, posOf[b]));&lt;br /&gt;
 &lt;br /&gt;
         if (posOf[a] &amp;gt; posOf[b])&lt;br /&gt;
             swap(a, b);&lt;br /&gt;
         return max(res, segmentTrees[pathOf[a]].getMax(posOf[a], posOf[b]));&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     void addValue(int v, int value) {&lt;br /&gt;
         segmentTrees[pathOf[v]].addValue(posOf[v], value);&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
== Ссылки на задачи ==&lt;br /&gt;
* [http://acm.timus.ru/problem.aspx?num=1553 Timus #1553 &amp;amp;mdash; Caves and Tunnels]&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
* [http://e-maxx.ru/algo/heavy_light e-maxx.ru &amp;amp;mdash; Heavy-light декомпозиция]&lt;br /&gt;
* [http://blog.anudeep2011.com/heavy-light-decomposition/ blog.anudeep2011.com &amp;amp;mdash; Heavy Light Decomposition]&lt;br /&gt;
* [http://github.com/indy256/codelibrary/blob/master/java/src/HeavyLight.java CodeLibrary &amp;amp;mdash; Heavy-light tree decomposition for edges or vertices]&lt;br /&gt;
* [http://github.com/ADJA/algos/blob/master/Graphs/HLD.cpp Algos &amp;amp;mdash; Heavy-light decomposition with segment trees in paths]&lt;br /&gt;
* [http://github.com/ADJA/algos/blob/master/Graphs/LCAHLD.cpp Algos &amp;amp;mdash; Finding LCA (Least common ancestor) of two vertices in the tree. Uses heavy-light decomposition]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Учебный курс «Алгоритмы и структуры данных»]]&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=%D0%9A%D0%B0%D1%82%D0%B5%D0%B3%D0%BE%D1%80%D0%B8%D1%8F:%D0%A3%D1%87%D0%B5%D0%B1%D0%BD%D1%8B%D0%B9_%D0%BA%D1%83%D1%80%D1%81_%C2%AB%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D1%8B_%D0%B8_%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D1%8B_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85%C2%BB&amp;diff=2942</id>
		<title>Категория:Учебный курс «Алгоритмы и структуры данных»</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=%D0%9A%D0%B0%D1%82%D0%B5%D0%B3%D0%BE%D1%80%D0%B8%D1%8F:%D0%A3%D1%87%D0%B5%D0%B1%D0%BD%D1%8B%D0%B9_%D0%BA%D1%83%D1%80%D1%81_%C2%AB%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D1%8B_%D0%B8_%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D1%8B_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85%C2%BB&amp;diff=2942"/>
		<updated>2026-01-12T18:26:18Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&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;
:* [[Поразрядная сортировка]]&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;
:* [[Множество. Реализация на битовых векторах]]&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;
:* 📄 [[Sparse table]]&lt;br /&gt;
:* Sqrt-декомпозиция&lt;br /&gt;
:* [[Алгоритм Мо]]&lt;br /&gt;
|width=450px|&lt;br /&gt;
;Алгоритмы для работы с графами&lt;br /&gt;
:&lt;br /&gt;
:* [[Основные определения. Представление графов]]&lt;br /&gt;
: Поиск в глубину и его приложения&lt;br /&gt;
:* 📄 [[Поиск в глубину]]&amp;lt;sup&amp;gt;&#039;&#039;O(V+E)&#039;&#039;&amp;lt;/sup&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;
:* 📄 [[Поиск в ширину]]&amp;lt;sup&amp;gt;&#039;&#039;O(V+E)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* 📝 [[Алгоритм Дейкстры]]&amp;lt;sup&amp;gt;&#039;&#039;O(V&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;+E) или O(ElogV)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* 📄 [[Алгоритм A*]]&lt;br /&gt;
:* [[Алгоритм Форда-Беллмана]]&amp;lt;sup&amp;gt;&#039;&#039;O(VE)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* [[Кратчайшие пути в ациклических орграфах]]&amp;lt;sup&amp;gt;&#039;&#039;O(V+E)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
: Кратчайшие пути между всеми парами вершин&lt;br /&gt;
:* 📄 [[Алгоритм Флойда]]&amp;lt;sup&amp;gt;&#039;&#039;O(V&amp;lt;sup&amp;gt;3&amp;lt;/sup&amp;gt;)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* [[Алгоритм Джонсона]]&amp;lt;sup&amp;gt;&#039;&#039;O(VElogV)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
: Минимальное остовное дерево&lt;br /&gt;
:* 📝 [[Алгоритм Краскала]]&amp;lt;sup&amp;gt;&#039;&#039;O(ElogV)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* 📄 [[Алгоритм Прима]]&amp;lt;sup&amp;gt;&#039;&#039;O(V&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;+E) или O(ElogV)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
: Максимальный поток&lt;br /&gt;
:* 📄 [[Алгоритм Форда-Фалкерсона]]&amp;lt;sup&amp;gt;&#039;&#039;O(Flow&amp;amp;times;E)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* 📄 [[Алгоритм Эдмондса-Карпа]]&amp;lt;sup&amp;gt;&#039;&#039;O(VE&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* 📄 [[Алгоритм Диница]]&amp;lt;sup&amp;gt;&#039;&#039;O(V&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;E)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* 📝 [[Максимальный поток минимальной стоимости]]&lt;br /&gt;
:* [[Применения максимального потока]]&lt;br /&gt;
: Максимальное паросочетание&lt;br /&gt;
:* 📄 [[Алгоритм Куна]]&amp;lt;sup&amp;gt;&#039;&#039;O(VE)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* 📄 [[Минимальное вершинное покрытие, максимальное независимое множество]]&lt;br /&gt;
: Наименьший общий предок&lt;br /&gt;
:* 📄 [[Метод двоичного подъёма]]&amp;lt;sup&amp;gt;&#039;&#039;O(NlogN), O(logN)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* [[Сведение LCA к RMQ и RMQ к LCA]]&lt;br /&gt;
:* [[Алгоритм Тарьяна (offline)]]&amp;lt;sup&amp;gt;&#039;&#039;O(N), O(1)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
: Декомпозиции деревьев&lt;br /&gt;
:* 📝 [[Heavy-light-декомпозиция]]&lt;br /&gt;
|&lt;br /&gt;
;Полный перебор и методы его оптимизации&lt;br /&gt;
:&lt;br /&gt;
:* [[Полный перебор]]&lt;br /&gt;
:* [[Два указателя]]&lt;br /&gt;
:* Meet in the middle&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;
:* 📄 [[Метод Гаусса]]&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;
:* 📄 [[Хеширование строк]]&lt;br /&gt;
:* 📄 [[Префикс-функция]]&lt;br /&gt;
:* 📄 [[Алгоритм Ахо-Корасик]]&lt;br /&gt;
:* 📄 [[Z-функция]]&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;
&#039;&#039;&amp;amp;copy; В. А. Фолунин, 2012–2026&#039;&#039;&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_A*&amp;diff=2941</id>
		<title>Алгоритм A*</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_A*&amp;diff=2941"/>
		<updated>2026-01-01T14:03:35Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Общая схема перехода от Дейкстры к A* ==&lt;br /&gt;
* Дейкстра ищет кратчайшие пути от заданной начальной вершины до всех остальных. A* ищет кратчайший путь от заданной начальной вершины до заданной конечной.&lt;br /&gt;
* Понадобится эвристическая функция h(v), делающая предположение о том, сколько ещё нужно пройти от вершины v до конечной вершины. Функция h() никогда не должна переоценивать это расстояние (но может недооценивать).&lt;br /&gt;
* В Дейкстре мы использовали массив dist[]: dist[v] — кратчайшее расстояние от стартовой вершины до вершины v. Теперь мы дополнительно используем массив distH[]: distH[v] — это оценённая длина пути из стартовой вершины в конечную через вершину v. Очевидно, distH[v] всегда равно dist[v] + h(v).&lt;br /&gt;
* Будем сравнивать непосещённые вершины и определять наиболее перспективного соседа не по dist[], а по distH[].&lt;br /&gt;
* Заметим, что если h() всегда возвращает 0, то A* превращается в алгоритм Дейкстры.&lt;br /&gt;
&lt;br /&gt;
{|width=100%&lt;br /&gt;
|width=50%|&lt;br /&gt;
 int dijkstra(vector&amp;lt;vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;&amp;gt; &amp;amp;graph, int start, int finish) {&lt;br /&gt;
     vector&amp;lt;int&amp;gt; dist(graph.size(), 1e9);&lt;br /&gt;
     set&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; q;&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
     dist[start] = 0;&lt;br /&gt;
     q.insert({ dist[start], start });&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
     while (!q.empty()) {&lt;br /&gt;
         int v = q.begin()-&amp;gt;second;&lt;br /&gt;
         q.erase(q.begin());&lt;br /&gt;
 &lt;br /&gt;
         if (v == finish)&lt;br /&gt;
             return dist[v];&lt;br /&gt;
 &lt;br /&gt;
         for (auto &amp;amp;[to, w] : graph[v]) {&lt;br /&gt;
             if (dist[to] &amp;gt; dist[v] + w) {&lt;br /&gt;
                 q.erase({ dist[to], to });&lt;br /&gt;
                 dist[to] = dist[v] + w;&lt;br /&gt;
                 q.insert({ dist[to], to });&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return 1e9;&lt;br /&gt;
 }&lt;br /&gt;
|width=50%|&lt;br /&gt;
 int aStar(vector&amp;lt;vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;&amp;gt; &amp;amp;graph, int start, int finish) {&lt;br /&gt;
     vector&amp;lt;int&amp;gt; dist(graph.size(), 1e9);&lt;br /&gt;
     {{Changed|1=vector&amp;lt;int&amp;gt; distH(graph.size(), 1e9);}}&lt;br /&gt;
     set&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; q;&lt;br /&gt;
 &lt;br /&gt;
     dist[start] = 0;&lt;br /&gt;
     {{Changed|1=distH[start] = dist[start] + h(start);}}&lt;br /&gt;
     q.insert({ {{Changed|distH}}[start], start });&lt;br /&gt;
 &lt;br /&gt;
     while (!q.empty()) {&lt;br /&gt;
         int v = q.begin()-&amp;gt;second;&lt;br /&gt;
         q.erase(q.begin());&lt;br /&gt;
 &lt;br /&gt;
         if (v == finish)&lt;br /&gt;
             return dist[v];&lt;br /&gt;
 &lt;br /&gt;
         for (auto &amp;amp;[to, w] : graph[v]) {&lt;br /&gt;
             if (dist[to] &amp;gt; dist[v] + w) {&lt;br /&gt;
                 q.erase({ {{Changed|distH}}[to], to });&lt;br /&gt;
                 dist[to] = dist[v] + w;&lt;br /&gt;
                 {{Changed|1=distH[to] = dist[to] + h(to);}}&lt;br /&gt;
                 q.insert({ {{Changed|distH}}[to], to });&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return 1e9;&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 15 Puzzle ==&lt;br /&gt;
&lt;br /&gt;
{|width=100%&lt;br /&gt;
|colspan=2|&lt;br /&gt;
&#039;&#039;&#039;Board&#039;&#039;&#039;&lt;br /&gt;
 const int SIDE = 4;&lt;br /&gt;
 const int SIZE = SIDE * SIDE;&lt;br /&gt;
 &lt;br /&gt;
 struct Board {&lt;br /&gt;
     vector&amp;lt;int&amp;gt; a;&lt;br /&gt;
     int zy, zx, manhattanHeuristic, unsolvable;&lt;br /&gt;
 &lt;br /&gt;
     void recalculate() {&lt;br /&gt;
         manhattanHeuristic = 0;&lt;br /&gt;
         int inv = 0;&lt;br /&gt;
         for (int y = 0; y &amp;lt; SIDE; y++) {&lt;br /&gt;
             for (int x = 0; x &amp;lt; SIDE; x++) {&lt;br /&gt;
                 if (a[y * SIDE + x]) {&lt;br /&gt;
                     for (int d = 0; d &amp;lt; y * SIDE + x; d++)&lt;br /&gt;
                         inv += a[d] &amp;amp;&amp;amp; a[d] &amp;gt; a[y * SIDE + x];&lt;br /&gt;
                 } else {&lt;br /&gt;
                     zy = y;&lt;br /&gt;
                     zx = x;&lt;br /&gt;
                 }&lt;br /&gt;
                 int tile = (a[y * SIDE + x] + SIZE - 1) % SIZE;&lt;br /&gt;
                 int ty = tile / SIDE, tx = tile % SIDE;&lt;br /&gt;
                 manhattanHeuristic += abs(y - ty) + abs(x - tx);&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         unsolvable = (inv + zy) % 2 == 0;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     bool operator &amp;lt; (const Board &amp;amp;that) const {&lt;br /&gt;
         if (manhattanHeuristic != that.manhattanHeuristic)&lt;br /&gt;
             return manhattanHeuristic &amp;lt; that.manhattanHeuristic;&lt;br /&gt;
         return a &amp;lt; that.a;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     bool canMove(int dy, int dx) {&lt;br /&gt;
         int ty = zy + dy, tx = zx + dx;&lt;br /&gt;
         return 0 &amp;lt;= ty &amp;amp;&amp;amp; ty &amp;lt; SIDE &amp;amp;&amp;amp; 0 &amp;lt;= tx &amp;amp;&amp;amp; tx &amp;lt; SIDE;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     Board move(int dy, int dx) {&lt;br /&gt;
         Board res = *this;&lt;br /&gt;
         int ty = zy + dy, tx = zx + dx;&lt;br /&gt;
         swap(res.a[zy * SIDE + zx], res.a[ty * SIDE + tx]);&lt;br /&gt;
         res.recalculate();&lt;br /&gt;
         return res;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 istream &amp;amp;operator &amp;gt;&amp;gt; (istream &amp;amp;in, Board &amp;amp;board) {&lt;br /&gt;
     board.a.resize(SIZE);&lt;br /&gt;
     for (int i = 0; i &amp;lt; SIZE; i++)&lt;br /&gt;
         in &amp;gt;&amp;gt; board.a[i];&lt;br /&gt;
     board.recalculate();&lt;br /&gt;
     return in;&lt;br /&gt;
 }&lt;br /&gt;
|-&lt;br /&gt;
|width=50%|&lt;br /&gt;
&#039;&#039;&#039;A*&#039;&#039;&#039;&lt;br /&gt;
 string aStar(Board &amp;amp;board) {&lt;br /&gt;
     if (board.unsolvable)&lt;br /&gt;
         return &amp;quot;NO SOLUTION&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
     map&amp;lt;Board, int&amp;gt; dist, distH, pred;&lt;br /&gt;
     set&amp;lt;pair&amp;lt;int, Board&amp;gt;&amp;gt; q;&lt;br /&gt;
 &lt;br /&gt;
     dist[board] = 0;&lt;br /&gt;
     distH[board] = dist[board] + board.manhattanHeuristic;&lt;br /&gt;
     pred[board] = -1;&lt;br /&gt;
     q.insert({ distH[board], board });&lt;br /&gt;
 &lt;br /&gt;
     static vector&amp;lt;int&amp;gt; dy = { -1, 0, 1, 0 };&lt;br /&gt;
     static vector&amp;lt;int&amp;gt; dx = { 0, 1, 0, -1 };&lt;br /&gt;
     static string dc = &amp;quot;URDL&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
     while (!q.empty()) {&lt;br /&gt;
         auto v = *q.begin()-&amp;gt;second;&lt;br /&gt;
         q.erase(q.begin());&lt;br /&gt;
 &lt;br /&gt;
         if (!v.manhattanHeuristic)&lt;br /&gt;
             break;&lt;br /&gt;
 &lt;br /&gt;
         for (int d = 0; d &amp;lt; 4; d++) {&lt;br /&gt;
             if (!v.canMove(dy[d], dx[d]))&lt;br /&gt;
                 continue;&lt;br /&gt;
 &lt;br /&gt;
             Board to = v.move(dy[d], dx[d]);&lt;br /&gt;
             &lt;br /&gt;
             if (auto it = dist.find(to); it == dist.end() || it-&amp;gt;second &amp;gt; dist[v] + 1) {&lt;br /&gt;
                 q.erase({ distH[to], to });&lt;br /&gt;
                 dist[to] = dist[v] + 1;&lt;br /&gt;
                 distH[to] = dist[to] + to.manhattanHeuristic;&lt;br /&gt;
                 pred[to] = d;&lt;br /&gt;
                 q.insert({ distH[to], to });&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     for (int i = 0; i &amp;lt; SIZE; i++)&lt;br /&gt;
         board.a[i] = (i + 1) % SIZE;&lt;br /&gt;
     board.recalculate();&lt;br /&gt;
 &lt;br /&gt;
     string path;&lt;br /&gt;
     while (1) {&lt;br /&gt;
         int d = pred[board];&lt;br /&gt;
         if (d == -1)&lt;br /&gt;
             break;&lt;br /&gt;
         path.push_back(dc[d]);&lt;br /&gt;
         board = board.move(dy[(d + 2) % 4], dx[(d + 2) % 4]);&lt;br /&gt;
     }&lt;br /&gt;
     reverse(path.begin(), path.end());&lt;br /&gt;
     return path;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 void solve() {&lt;br /&gt;
     Board board;&lt;br /&gt;
     cin &amp;gt;&amp;gt; board;&lt;br /&gt;
     cout &amp;lt;&amp;lt; aStar(board) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
 }&lt;br /&gt;
|width=50%|&lt;br /&gt;
&#039;&#039;&#039;IDA*&#039;&#039;&#039;&lt;br /&gt;
 struct Solver {&lt;br /&gt;
     map&amp;lt;Board, int&amp;gt; visited, pred;&lt;br /&gt;
 &lt;br /&gt;
     static inline vector&amp;lt;int&amp;gt; dy = { -1, 0, 1, 0 };&lt;br /&gt;
     static inline vector&amp;lt;int&amp;gt; dx = { 0, 1, 0, -1 };&lt;br /&gt;
     static inline string dc = &amp;quot;URDL&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
     bool dfs(Board &amp;amp;v, int depth, int limit, int &amp;amp;nextLimit) {&lt;br /&gt;
         if (depth + v.manhattanHeuristic &amp;gt; limit) {&lt;br /&gt;
             nextLimit = min(nextLimit, depth + v.manhattanHeuristic);&lt;br /&gt;
             return 0;&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         if (!v.manhattanHeuristic)&lt;br /&gt;
             return 1;&lt;br /&gt;
 &lt;br /&gt;
         if (auto it = visited.find(v); it != visited.end() &amp;amp;&amp;amp; it-&amp;gt;second &amp;lt;= depth)&lt;br /&gt;
             return 0;&lt;br /&gt;
         visited[v] = depth;&lt;br /&gt;
 &lt;br /&gt;
         for (int d = 0; d &amp;lt; 4; d++) {&lt;br /&gt;
             if (!v.canMove(dy[d], dx[d]))&lt;br /&gt;
                 continue;&lt;br /&gt;
 &lt;br /&gt;
             Board to = v.move(dy[d], dx[d]);&lt;br /&gt;
 &lt;br /&gt;
             if (dfs(to, depth + 1, limit, nextLimit)) {&lt;br /&gt;
                 pred[to] = d;&lt;br /&gt;
                 return 1;&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         return 0;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     string idaStar(Board &amp;amp;board) {&lt;br /&gt;
         if (board.unsolvable)&lt;br /&gt;
             return &amp;quot;NO SOLUTION&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
         int limit = 0;&lt;br /&gt;
         while (1) {&lt;br /&gt;
             int nextLimit = 1e9;&lt;br /&gt;
             visited.clear();&lt;br /&gt;
             pred = { { board, -1 } };&lt;br /&gt;
    &lt;br /&gt;
             if (dfs(board, 0, limit, nextLimit))&lt;br /&gt;
                 break;&lt;br /&gt;
 &lt;br /&gt;
             limit = nextLimit;&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         for (int i = 0; i &amp;lt; SIZE; i++)&lt;br /&gt;
             board.a[i] = (i + 1) % SIZE;&lt;br /&gt;
         board.recalculate();&lt;br /&gt;
 &lt;br /&gt;
         string path;&lt;br /&gt;
         while (1) {&lt;br /&gt;
             int d = pred[board];&lt;br /&gt;
             if (d == -1)&lt;br /&gt;
                 break;&lt;br /&gt;
             path.push_back(dc[d]);&lt;br /&gt;
             board = board.move(dy[(d + 2) % 4], dx[(d + 2) % 4]);&lt;br /&gt;
         }&lt;br /&gt;
         reverse(path.begin(), path.end());&lt;br /&gt;
         return path;&lt;br /&gt;
     }&lt;br /&gt;
 } solver;&lt;br /&gt;
 &lt;br /&gt;
 void solve() {&lt;br /&gt;
     Board board;&lt;br /&gt;
     cin &amp;gt;&amp;gt; board;&lt;br /&gt;
     cout &amp;lt;&amp;lt; solver.idaStar(board) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Knight Path ==&lt;br /&gt;
&lt;br /&gt;
 int aStar(int size, int sy, int sx, int fy, int fx) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; dist(size, vector&amp;lt;int&amp;gt;(size, 1e9));&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; distH(size, vector&amp;lt;int&amp;gt;(size, 1e9));&lt;br /&gt;
     set&amp;lt;pair&amp;lt;int, pair&amp;lt;int, int&amp;gt;&amp;gt;&amp;gt; q;&lt;br /&gt;
 &lt;br /&gt;
     dist[sy][sx] = 0;&lt;br /&gt;
     distH[sy][sx] = dist[sy][sx] + (abs(sy - fy) + abs(sx - fx)) / 3;&lt;br /&gt;
     q.insert({ distH[sy][sx], { sy, sx } });&lt;br /&gt;
 &lt;br /&gt;
     while (!q.empty()) {&lt;br /&gt;
         auto [y, x] = q.begin()-&amp;gt;second;&lt;br /&gt;
         q.erase(q.begin());&lt;br /&gt;
 &lt;br /&gt;
         if (y == fy &amp;amp;&amp;amp; x == fx)&lt;br /&gt;
             return dist[y][x];&lt;br /&gt;
 &lt;br /&gt;
         static int dy[] = { -2, -2, -1, 1, 2, 2, 1, -1 };&lt;br /&gt;
         static int dx[] = { -1, 1, 2, 2, 1, -1, -2, -2 };&lt;br /&gt;
 &lt;br /&gt;
         for (int d = 0; d &amp;lt; 8; d++) {&lt;br /&gt;
             int ty = y + dy[d], tx = x + dx[d];&lt;br /&gt;
             if (0 &amp;lt;= ty &amp;amp;&amp;amp; ty &amp;lt; size &amp;amp;&amp;amp; 0 &amp;lt;= tx &amp;amp;&amp;amp; tx &amp;lt; size &amp;amp;&amp;amp; dist[ty][tx] &amp;gt; dist[y][x] + 1) {&lt;br /&gt;
                 q.erase({ distH[ty][tx], { ty, tx } });&lt;br /&gt;
                 dist[ty][tx] = dist[y][x] + 1;&lt;br /&gt;
                 distH[ty][tx] = dist[ty][tx] + (abs(ty - fy) + abs(tx - fx)) / 3;&lt;br /&gt;
                 q.insert({ distH[ty][tx], { ty, tx } });&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return 1e9;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
* [http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_A* neerc.ifmo.ru/wiki — Алгоритм A*]&lt;br /&gt;
* [http://www.redblobgames.com/pathfinding/a-star/introduction.html Red Blob Games — Introduction to the A* Algorithm]&lt;br /&gt;
* [http://www.redblobgames.com/pathfinding/a-star/implementation.html Red Blob Games — Implementation of A*]&lt;br /&gt;
* [http://theory.stanford.edu/~amitp/GameProgramming/ Red Blob Games — Amit’s A* Pages]&lt;br /&gt;
* [http://brilliant.org/wiki/a-star-search Brilliant.org — A* Search]&lt;br /&gt;
* [http://www.cs.princeton.edu/courses/archive/fall19/cos226/assignments/8puzzle/specification.php Princeton COS 226 Assignment — 8 Slider Puzzle]&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=%D0%A5%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D1%81%D1%82%D1%80%D0%BE%D0%BA&amp;diff=2940</id>
		<title>Хеширование строк</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=%D0%A5%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D1%81%D1%82%D1%80%D0%BE%D0%BA&amp;diff=2940"/>
		<updated>2025-12-18T01:48:21Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;h&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt; = s&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt;x&amp;lt;sup&amp;gt;n - 1&amp;lt;/sup&amp;gt; + s&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt;x&amp;lt;sup&amp;gt;n - 2&amp;lt;/sup&amp;gt; + ... + s&amp;lt;sub&amp;gt;n - 2&amp;lt;/sub&amp;gt;x + s&amp;lt;sub&amp;gt;n - 1&amp;lt;/sub&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 struct Hasher {&lt;br /&gt;
     long long mod;&lt;br /&gt;
     vector&amp;lt;long long&amp;gt; p, h;&lt;br /&gt;
 &lt;br /&gt;
     Hasher(const string &amp;amp;s, long long factor = 31, long long mod = 1e9 + 7) : mod(mod) {&lt;br /&gt;
         p.push_back(1);&lt;br /&gt;
         for (int i = 1; i &amp;lt; s.size(); i++)&lt;br /&gt;
             p.push_back(p[i - 1] * factor % mod);&lt;br /&gt;
 &lt;br /&gt;
         h.push_back(s[0] - &#039;a&#039; + 1);&lt;br /&gt;
         for (int i = 1; i &amp;lt; s.size(); i++)&lt;br /&gt;
             h.push_back((h[i - 1] * factor % mod + s[i] - &#039;a&#039; + 1) % mod);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     long long getHash(int l, int r) {&lt;br /&gt;
         long long res = h[r];&lt;br /&gt;
         if (l)&lt;br /&gt;
             res = (res - p[r - l + 1] * h[l - 1] % mod + mod) % mod;&lt;br /&gt;
         return res;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
 struct DoubleHasher { &lt;br /&gt;
     Hasher h1, h2;&lt;br /&gt;
 &lt;br /&gt;
     DoubleHasher(const string &amp;amp;s) : h1(s), h2(s, 29, 1e9 + 9) {}&lt;br /&gt;
 &lt;br /&gt;
     pair&amp;lt;long long, long long&amp;gt; getHash(int l, int r) {&lt;br /&gt;
         return { h1.getHash(l, r), h2.getHash(l, r) };&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
 struct Hasher2D {&lt;br /&gt;
     long long mod;&lt;br /&gt;
     vector&amp;lt;long long&amp;gt; py, px;&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;long long&amp;gt;&amp;gt; h;&lt;br /&gt;
 &lt;br /&gt;
     Hasher2D(const vector&amp;lt;string&amp;gt; &amp;amp;a, long long factorY = 29, long long factorX = 31, long long mod = 1e9 + 7) : mod(mod) {&lt;br /&gt;
         py.push_back(1);&lt;br /&gt;
         for (int y = 1; y &amp;lt; a.size(); y++)&lt;br /&gt;
             py.push_back(py[y - 1] * factorY % mod);&lt;br /&gt;
 &lt;br /&gt;
         px.push_back(1);&lt;br /&gt;
         for (int x = 1; x &amp;lt; a[0].size(); x++)&lt;br /&gt;
             px.push_back(px[x - 1] * factorX % mod);&lt;br /&gt;
 &lt;br /&gt;
         h.assign(a.size(), vector&amp;lt;long long&amp;gt;(a[0].size()));&lt;br /&gt;
         for (int y = 0; y &amp;lt; a.size(); y++) {&lt;br /&gt;
             for (int x = 0; x &amp;lt; a[0].size(); x++) {&lt;br /&gt;
                 h[y][x] = (a[y][x] - &#039;a&#039; + 1) % mod;&lt;br /&gt;
                 if (y)&lt;br /&gt;
                     h[y][x] = (h[y][x] + h[y - 1][x] * factorY) % mod;&lt;br /&gt;
                 if (x)&lt;br /&gt;
                     h[y][x] = (h[y][x] + h[y][x - 1] * factorX) % mod;&lt;br /&gt;
                 if (y &amp;amp;&amp;amp; x)&lt;br /&gt;
                     h[y][x] = ((h[y][x] - h[y - 1][x - 1] * factorY * factorX) % mod + mod) % mod;&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     long long getHash(int yl, int xl, int yr, int xr) {&lt;br /&gt;
         long long res = h[yr][xr];&lt;br /&gt;
         if (yl)&lt;br /&gt;
             res = (res - py[yr - yl + 1] * h[yl - 1][xr] % mod + mod) % mod;&lt;br /&gt;
         if (xl)&lt;br /&gt;
             res = (res - px[xr - xl + 1] * h[yr][xl - 1] % mod + mod) % mod;&lt;br /&gt;
         if (yl &amp;amp;&amp;amp; xl)&lt;br /&gt;
             res = (res + py[yr - yl + 1] * px[xr - xl + 1] % mod * h[yl - 1][xl - 1]) % mod;&lt;br /&gt;
         return res;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--{| width=&amp;quot;100%&amp;quot;&lt;br /&gt;
| width=50% |&lt;br /&gt;
h&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt; = s&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; + s&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt;x + s&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt;x&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt; + ... + s&amp;lt;sub&amp;gt;n - 1&amp;lt;/sub&amp;gt;x&amp;lt;sup&amp;gt;n - 1&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для выравнивания хеши различных подстрок домножаются на x&amp;lt;sup&amp;gt;n - 1 - L&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 struct Hasher {&lt;br /&gt;
     long long x = 31, mod = 1e9 + 7;&lt;br /&gt;
     vector&amp;lt;long long&amp;gt; p, h;&lt;br /&gt;
 &lt;br /&gt;
     Hasher(const string &amp;amp;s) {&lt;br /&gt;
         p.resize(s.size());&lt;br /&gt;
         h.resize(s.size());&lt;br /&gt;
 &lt;br /&gt;
         p[0] = 1;&lt;br /&gt;
         h[0] = s[0] - &#039;a&#039; + 1;&lt;br /&gt;
 &lt;br /&gt;
         for (int i = 1; i &amp;lt; s.size(); i++) {&lt;br /&gt;
             p[i] = p[i - 1] * x % mod;&lt;br /&gt;
             h[i] = (h[i - 1] + p[i] * (s[i] - &#039;a&#039; + 1) % mod) % mod;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     long long getHash(int l, int r) {&lt;br /&gt;
         long long res = h[r];&lt;br /&gt;
         if (l)&lt;br /&gt;
             res = (res - h[l - 1] + mod) % mod;&lt;br /&gt;
         res = res * p[p.size() - 1 - l] % mod;&lt;br /&gt;
         return res;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
Этот способ непригоден для сравнения участков двух строк разной длины. В данном случае хеш более левого участка следует домножать на x&amp;lt;sup&amp;gt;|L1 - L2|&amp;lt;/sup&amp;gt;.&lt;br /&gt;
 &lt;br /&gt;
| width=&amp;quot;10px&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| width=50% |&lt;br /&gt;
h&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt; = s&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; + s&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt;x + s&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt;x&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt; + ... + s&amp;lt;sub&amp;gt;n - 1&amp;lt;/sub&amp;gt;x&amp;lt;sup&amp;gt;n - 1&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для выравнивания хеши различных подстрок домножаются на (x&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;)&amp;lt;sup&amp;gt;L&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 struct Hasher {&lt;br /&gt;
     long long x = 31, {{Changed|1=xi = 129032259,}} mod = 1e9 + 7;&lt;br /&gt;
     vector&amp;lt;long long&amp;gt; p, {{Changed|pi,}} h;&lt;br /&gt;
 &lt;br /&gt;
     Hasher(const string &amp;amp;s) {&lt;br /&gt;
         p.resize(s.size());&lt;br /&gt;
         {{Changed|pi.resize(s.size());}}&lt;br /&gt;
         h.resize(s.size());&lt;br /&gt;
 &lt;br /&gt;
         p[0] = {{Changed|1=pi[0] = }} 1;&lt;br /&gt;
         h[0] = s[0] - &#039;a&#039; + 1;&lt;br /&gt;
 &lt;br /&gt;
         for (int i = 1; i &amp;lt; s.size(); i++) {&lt;br /&gt;
             p[i] = p[i - 1] * x % mod;&lt;br /&gt;
             {{Changed|1=pi[i] = pi[i - 1] * xi % mod;}}&lt;br /&gt;
             h[i] = (h[i - 1] + p[i] * (s[i] - &#039;a&#039; + 1) % mod) % mod;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     long long getHash(int l, int r) {&lt;br /&gt;
         long long res = h[r];&lt;br /&gt;
         if (l) {&lt;br /&gt;
             res = (res - h[l - 1] + mod) % mod;&lt;br /&gt;
             {{Changed|1=res = (res * pi[l]) % mod}};&lt;br /&gt;
         }&lt;br /&gt;
         return res;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
|}--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
Теория:&lt;br /&gt;
* [http://e-maxx.ru/algo/string_hashes e-maxx.ru &amp;amp;mdash; Алгоритмы хэширования в задачах на строки]&lt;br /&gt;
* [http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8_%D0%B2_%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B5_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%85%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F._%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A0%D0%B0%D0%B1%D0%B8%D0%BD%D0%B0-%D0%9A%D0%B0%D1%80%D0%BF%D0%B0 neerc.ifmo.ru &amp;amp;mdash; Поиск подстроки в строке с использованием хеширования. Алгоритм Рабина-Карпа]&lt;br /&gt;
* [http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BD%D0%B0%D0%B8%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B5%D0%B9_%D0%BE%D0%B1%D1%89%D0%B5%D0%B9_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8_%D0%B4%D0%B2%D1%83%D1%85_%D1%81%D1%82%D1%80%D0%BE%D0%BA_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%85%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F neerc.ifmo.ru &amp;amp;mdash; Поиск наибольшей общей подстроки двух строк с использованием хеширования]&lt;br /&gt;
* [https://ru.algorithmica.org/cs/hashing/polynomial/ ru.algorithmica.org — Полиномиальное хеширование]&lt;br /&gt;
* [http://habrahabr.ru/post/142589/ habrahabr.ru &amp;amp;mdash; Полиномиальные хеши и их применение]&lt;br /&gt;
* [http://codeforces.com/blog/entry/4898 codeforces.com &amp;amp;mdash; Anti-hash test]&lt;br /&gt;
* [http://codeforces.com/blog/entry/60445 codeforces.com — Полиномиальное хеширование + разбор интересных задач]&lt;br /&gt;
* [https://blog.algoprog.ru/hash-no-multiply/ Калинин П. — Про хеширование без домножения]&lt;br /&gt;
* [https://usaco.guide/gold/string-hashing?lang=cpp usaco.guide — String Hashing]&lt;br /&gt;
Код:&lt;br /&gt;
* [https://github.com/indy256/codelibrary/blob/master/cpp/strings/hashing.cpp codelibrary/cpp/strings/hashing.cpp]&lt;br /&gt;
* [https://github.com/ADJA/algos/blob/master/Strings/Hashing.cpp algos/Strings/Hashing.cpp]&lt;br /&gt;
Задачи:&lt;br /&gt;
* [[:Категория: Задачи: Хеширование строк|Задачи: Хеширование строк]]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Строки]]&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=%D0%A5%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D1%81%D1%82%D1%80%D0%BE%D0%BA&amp;diff=2939</id>
		<title>Хеширование строк</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=%D0%A5%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D1%81%D1%82%D1%80%D0%BE%D0%BA&amp;diff=2939"/>
		<updated>2025-12-18T01:28:59Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;h&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt; = s&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt;x&amp;lt;sup&amp;gt;n - 1&amp;lt;/sup&amp;gt; + s&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt;x&amp;lt;sup&amp;gt;n - 2&amp;lt;/sup&amp;gt; + ... + s&amp;lt;sub&amp;gt;n - 2&amp;lt;/sub&amp;gt;x + s&amp;lt;sub&amp;gt;n - 1&amp;lt;/sub&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 struct Hasher {&lt;br /&gt;
     long long mod;&lt;br /&gt;
     vector&amp;lt;long long&amp;gt; p, h;&lt;br /&gt;
 &lt;br /&gt;
     Hasher(const string &amp;amp;s, long long factor = 31, long long mod = 1e9 + 7) : mod(mod) {&lt;br /&gt;
         p.push_back(1);&lt;br /&gt;
         for (int i = 1; i &amp;lt; s.size(); i++)&lt;br /&gt;
             p.push_back(p[i - 1] * factor % mod);&lt;br /&gt;
 &lt;br /&gt;
         h.push_back(s[0] - &#039;a&#039; + 1);&lt;br /&gt;
         for (int i = 1; i &amp;lt; s.size(); i++)&lt;br /&gt;
             h.push_back((h[i - 1] * factor % mod + s[i] - &#039;a&#039; + 1) % mod);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     long long getHash(int l, int r) {&lt;br /&gt;
         long long res = h[r];&lt;br /&gt;
         if (l)&lt;br /&gt;
             res = (res - p[r - l + 1] * h[l - 1] % mod + mod) % mod;&lt;br /&gt;
         return res;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
 struct DoubleHasher { &lt;br /&gt;
     Hasher h1, h2;&lt;br /&gt;
 &lt;br /&gt;
     DoubleHasher(const string &amp;amp;s) : h1(s), h2(s, 29, 1e9 + 9) {}&lt;br /&gt;
 &lt;br /&gt;
     pair&amp;lt;long long, long long&amp;gt; getHash(int l, int r) {&lt;br /&gt;
         return { h1.getHash(l, r), h2.getHash(l, r) };&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
 struct Hasher2D {&lt;br /&gt;
     long long mod;&lt;br /&gt;
     vector&amp;lt;long long&amp;gt; py, px;&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;long long&amp;gt;&amp;gt; h;&lt;br /&gt;
 &lt;br /&gt;
     Hasher2D(const vector&amp;lt;string&amp;gt; &amp;amp;a, long long factorY = 29, long long factorX = 31, long long mod = 1e9 + 7) : mod(mod) {&lt;br /&gt;
         py.push_back(1);&lt;br /&gt;
         for (int y = 1; y &amp;lt; a.size(); y++)&lt;br /&gt;
             py.push_back(py[y - 1] * factorY % mod);&lt;br /&gt;
 &lt;br /&gt;
         px.push_back(1);&lt;br /&gt;
         for (int x = 1; x &amp;lt; a[0].size(); x++)&lt;br /&gt;
             px.push_back(px[x - 1] * factorX % mod);&lt;br /&gt;
 &lt;br /&gt;
         h.assign(a.size(), vector&amp;lt;long long&amp;gt;(a[0].size()));&lt;br /&gt;
         for (int y = 0; y &amp;lt; a.size(); y++) {&lt;br /&gt;
             for (int x = 0; x &amp;lt; a[0].size(); x++) {&lt;br /&gt;
                 h[y][x] = (a[y][x] - &#039;a&#039; + 1) % mod;&lt;br /&gt;
                 if (y)&lt;br /&gt;
                     h[y][x] = (h[y][x] + h[y - 1][x] * factorY) % mod;&lt;br /&gt;
                 if (x)&lt;br /&gt;
                     h[y][x] = (h[y][x] + h[y][x - 1] * factorX) % mod;&lt;br /&gt;
                 if (y &amp;amp;&amp;amp; x)&lt;br /&gt;
                     h[y][x] = ((h[y][x] - h[y - 1][x - 1] * factorY * factorX) % mod + mod) % mod;&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     long long getHash(int yl, int xl, int yr, int xr) {&lt;br /&gt;
         long long res = h[yr][xr];&lt;br /&gt;
         if (yl)&lt;br /&gt;
             res = (res - py[yr - yl + 1] * h[yl - 1][xr] % mod + mod) % mod;&lt;br /&gt;
         if (xl)&lt;br /&gt;
             res = (res - px[xr - xl + 1] * h[yr][xl - 1] % mod + mod) % mod;&lt;br /&gt;
         if (yl &amp;amp;&amp;amp; xl)&lt;br /&gt;
             res = (res + py[yr - yl + 1] * px[xr - xl + 1] % mod * h[yl - 1][xl - 1]) % mod;&lt;br /&gt;
         return res;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
{| width=&amp;quot;100%&amp;quot;&lt;br /&gt;
| width=50% |&lt;br /&gt;
h&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt; = s&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; + s&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt;x + s&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt;x&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt; + ... + s&amp;lt;sub&amp;gt;n - 1&amp;lt;/sub&amp;gt;x&amp;lt;sup&amp;gt;n - 1&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для выравнивания хеши различных подстрок домножаются на x&amp;lt;sup&amp;gt;n - 1 - L&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 struct Hasher {&lt;br /&gt;
     long long x = 31, mod = 1e9 + 7;&lt;br /&gt;
     vector&amp;lt;long long&amp;gt; p, h;&lt;br /&gt;
 &lt;br /&gt;
     Hasher(const string &amp;amp;s) {&lt;br /&gt;
         p.resize(s.size());&lt;br /&gt;
         h.resize(s.size());&lt;br /&gt;
 &lt;br /&gt;
         p[0] = 1;&lt;br /&gt;
         h[0] = s[0] - &#039;a&#039; + 1;&lt;br /&gt;
 &lt;br /&gt;
         for (int i = 1; i &amp;lt; s.size(); i++) {&lt;br /&gt;
             p[i] = p[i - 1] * x % mod;&lt;br /&gt;
             h[i] = (h[i - 1] + p[i] * (s[i] - &#039;a&#039; + 1) % mod) % mod;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     long long getHash(int l, int r) {&lt;br /&gt;
         long long res = h[r];&lt;br /&gt;
         if (l)&lt;br /&gt;
             res = (res - h[l - 1] + mod) % mod;&lt;br /&gt;
         res = res * p[p.size() - 1 - l] % mod;&lt;br /&gt;
         return res;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
Этот способ непригоден для сравнения участков двух строк разной длины. В данном случае хеш более левого участка следует домножать на x&amp;lt;sup&amp;gt;|L1 - L2|&amp;lt;/sup&amp;gt;.&lt;br /&gt;
 &lt;br /&gt;
| width=&amp;quot;10px&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| width=50% |&lt;br /&gt;
h&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt; = s&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; + s&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt;x + s&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt;x&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt; + ... + s&amp;lt;sub&amp;gt;n - 1&amp;lt;/sub&amp;gt;x&amp;lt;sup&amp;gt;n - 1&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для выравнивания хеши различных подстрок домножаются на (x&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;)&amp;lt;sup&amp;gt;L&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 struct Hasher {&lt;br /&gt;
     long long x = 31, {{Changed|1=xi = 129032259,}} mod = 1e9 + 7;&lt;br /&gt;
     vector&amp;lt;long long&amp;gt; p, {{Changed|pi,}} h;&lt;br /&gt;
 &lt;br /&gt;
     Hasher(const string &amp;amp;s) {&lt;br /&gt;
         p.resize(s.size());&lt;br /&gt;
         {{Changed|pi.resize(s.size());}}&lt;br /&gt;
         h.resize(s.size());&lt;br /&gt;
 &lt;br /&gt;
         p[0] = {{Changed|1=pi[0] = }} 1;&lt;br /&gt;
         h[0] = s[0] - &#039;a&#039; + 1;&lt;br /&gt;
 &lt;br /&gt;
         for (int i = 1; i &amp;lt; s.size(); i++) {&lt;br /&gt;
             p[i] = p[i - 1] * x % mod;&lt;br /&gt;
             {{Changed|1=pi[i] = pi[i - 1] * xi % mod;}}&lt;br /&gt;
             h[i] = (h[i - 1] + p[i] * (s[i] - &#039;a&#039; + 1) % mod) % mod;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     long long getHash(int l, int r) {&lt;br /&gt;
         long long res = h[r];&lt;br /&gt;
         if (l) {&lt;br /&gt;
             res = (res - h[l - 1] + mod) % mod;&lt;br /&gt;
             {{Changed|1=res = (res * pi[l]) % mod}};&lt;br /&gt;
         }&lt;br /&gt;
         return res;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
Теория:&lt;br /&gt;
* [http://e-maxx.ru/algo/string_hashes e-maxx.ru &amp;amp;mdash; Алгоритмы хэширования в задачах на строки]&lt;br /&gt;
* [http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8_%D0%B2_%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B5_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%85%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F._%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A0%D0%B0%D0%B1%D0%B8%D0%BD%D0%B0-%D0%9A%D0%B0%D1%80%D0%BF%D0%B0 neerc.ifmo.ru &amp;amp;mdash; Поиск подстроки в строке с использованием хеширования. Алгоритм Рабина-Карпа]&lt;br /&gt;
* [http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BD%D0%B0%D0%B8%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B5%D0%B9_%D0%BE%D0%B1%D1%89%D0%B5%D0%B9_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8_%D0%B4%D0%B2%D1%83%D1%85_%D1%81%D1%82%D1%80%D0%BE%D0%BA_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%85%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F neerc.ifmo.ru &amp;amp;mdash; Поиск наибольшей общей подстроки двух строк с использованием хеширования]&lt;br /&gt;
* [https://ru.algorithmica.org/cs/hashing/polynomial/ ru.algorithmica.org — Полиномиальное хеширование]&lt;br /&gt;
* [http://habrahabr.ru/post/142589/ habrahabr.ru &amp;amp;mdash; Полиномиальные хеши и их применение]&lt;br /&gt;
* [http://codeforces.com/blog/entry/4898 codeforces.com &amp;amp;mdash; Anti-hash test]&lt;br /&gt;
* [http://codeforces.com/blog/entry/60445 codeforces.com — Полиномиальное хеширование + разбор интересных задач]&lt;br /&gt;
* [https://blog.algoprog.ru/hash-no-multiply/ Калинин П. — Про хеширование без домножения]&lt;br /&gt;
* [https://usaco.guide/gold/string-hashing?lang=cpp usaco.guide — String Hashing]&lt;br /&gt;
Код:&lt;br /&gt;
* [https://github.com/indy256/codelibrary/blob/master/cpp/strings/hashing.cpp codelibrary/cpp/strings/hashing.cpp]&lt;br /&gt;
* [https://github.com/ADJA/algos/blob/master/Strings/Hashing.cpp algos/Strings/Hashing.cpp]&lt;br /&gt;
Задачи:&lt;br /&gt;
* [[:Категория: Задачи: Хеширование строк|Задачи: Хеширование строк]]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Строки]]&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=%D0%97%D0%B0%D0%B4%D0%B0%D1%87%D0%B0_%D0%BE_%D1%80%D1%8E%D0%BA%D0%B7%D0%B0%D0%BA%D0%B5_%D0%B8_%D1%81%D0%B2%D1%8F%D0%B7%D0%B0%D0%BD%D0%BD%D1%8B%D0%B5_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8&amp;diff=2938</id>
		<title>Задача о рюкзаке и связанные задачи</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=%D0%97%D0%B0%D0%B4%D0%B0%D1%87%D0%B0_%D0%BE_%D1%80%D1%8E%D0%BA%D0%B7%D0%B0%D0%BA%D0%B5_%D0%B8_%D1%81%D0%B2%D1%8F%D0%B7%D0%B0%D0%BD%D0%BD%D1%8B%D0%B5_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8&amp;diff=2938"/>
		<updated>2025-11-19T00:38:03Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: /* Каким минимальным количеством предметом можно набрать сумму targetSum */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__TOC__ &lt;br /&gt;
&lt;br /&gt;
== Задача о сумме подмножеств (subset sum problem) ==&lt;br /&gt;
&lt;br /&gt;
=== Можно ли набрать заданный вес ===&lt;br /&gt;
Есть несколько предметов, для каждого известен вес. Можем ли мы выбрать несколько предметов так, чтобы общий вес был в точности равен заданному числу?&lt;br /&gt;
&lt;br /&gt;
 vector&amp;lt;int&amp;gt; itemWeight; // веса предметов&lt;br /&gt;
 int targetWeight;       // вес, который нужно набрать&lt;br /&gt;
&lt;br /&gt;
Будем заполнять таблицу canMake[][]. Строки таблицы соответствуют количеству рассмотренных предметов: нулевая строка — ноль предметов, первая строка — один предмет, ..., N-я строка — N предметов. Столбцы таблицы соответствуют весам от 0 до заданного веса. В ячейках хранятся true и false: canMake[items][weight] == true, если мы можем набрать вес weight, используя некоторые из items первых предметов.&lt;br /&gt;
&lt;br /&gt;
 vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; canMake(itemWeight.size() + 1, vector&amp;lt;int&amp;gt;(targetWeight + 1));&lt;br /&gt;
&lt;br /&gt;
Как должна выглядеть нулевая строка таблицы? В нулевой строке мы описываем ситуацию, когда не рассмотрено ещё ни одного предмета; какие веса мы при этом можем набрать? Очевидно, только вес 0. Поэтому ячейка canMake[0][0] должна содержать true, а остальные ячейки первой строки — false.&lt;br /&gt;
&lt;br /&gt;
       суммарные веса  0  1  2  3  4  5  6  7  8  9 10&lt;br /&gt;
    строка canMake[0]  1  0  0  0  0  0  0  0  0  0  0 &lt;br /&gt;
 (предметов пока нет)&lt;br /&gt;
&lt;br /&gt;
Теперь разберёмся, как должна выглядеть первая строка таблицы (она соответствует первому предмету). Пусть, для определённости, вес первого предмета — 3 (itemWeight[0] == 3). Какие веса мы теперь можем набрать?&lt;br /&gt;
&lt;br /&gt;
Нам всё ещё доступен вес 0 (если мы не будем брать первый предмет), но теперь мы можем получить ещё и вес 3 (если мы возьмём первый предмет).&lt;br /&gt;
&lt;br /&gt;
       суммарные веса  0  1  2  3  4  5  6  7  8  9 10&lt;br /&gt;
    строка canMake[0]  1  0  0  0  0  0  0  0  0  0  0&lt;br /&gt;
 (предметов пока нет)&lt;br /&gt;
    строка canMake[1]  1  0  0  1  0  0  0  0  0  0  0&lt;br /&gt;
   (вес предмета — 3) &lt;br /&gt;
&lt;br /&gt;
Далее — вторая строка таблицы (она соответствует первым двум предметам). Пусть вес второго предмета — 5 (itemWeight[1] == 5). Какие веса мы теперь можем набрать?&lt;br /&gt;
&lt;br /&gt;
Нам всё ещё доступны веса 0 и 3 (если мы не будем брать второй предмет), но теперь мы можем получить ещё веса 5 (если мы до этого не брали ничего, а теперь возьмём второй предмет) и 8 (если мы до этого взяли первый предмет и получили вес 3, а сейчас добавим второй предмет).&lt;br /&gt;
&lt;br /&gt;
       суммарные веса  0  1  2  3  4  5  6  7  8  9 10&lt;br /&gt;
    строка canMake[0]  1  0  0  0  0  0  0  0  0  0  0&lt;br /&gt;
 (предметов пока нет)&lt;br /&gt;
    строка canMake[1]  1  0  0  1  0  0  0  0  0  0  0&lt;br /&gt;
   (вес предмета — 3) &lt;br /&gt;
    строка canMake[2]  1  0  0  1  0  1  0  0  1  0  0&lt;br /&gt;
   (вес предмета — 5)&lt;br /&gt;
&lt;br /&gt;
Далее — третья строка таблицы (она соответствует первым трём предметам). Пусть вес третьего предмета — 2 (itemWeight[2] == 2).&lt;br /&gt;
&lt;br /&gt;
Раньше (без третьего предмета) мы могли набрать веса 0, 3, 5 и 8. Теперь (с третьим предметом) можем дополнительно набрать 2, 5, 7 и 10. Обратите внимание, что вес 5 мы могли набрать как без третьего предмета (просто взяв второй предмет с весом 5), так и с ним (взяв первый и третий предметы с весами 3 и 2); ничего необычного в этой ситуации нет.&lt;br /&gt;
&lt;br /&gt;
       суммарные веса  0  1  2  3  4  5  6  7  8  9 10&lt;br /&gt;
    строка canMake[0]  1  0  0  0  0  0  0  0  0  0  0&lt;br /&gt;
 (предметов пока нет)&lt;br /&gt;
    строка canMake[1]  1  0  0  1  0  0  0  0  0  0  0&lt;br /&gt;
   (вес предмета — 3) &lt;br /&gt;
    строка canMake[2]  1  0  0  1  0  1  0  0  1  0  0&lt;br /&gt;
   (вес предмета — 5)&lt;br /&gt;
    строка canMake[3]  1  0  1  1  0  1  0  1  1  0  1&lt;br /&gt;
   (вес предмета — 2)&lt;br /&gt;
&lt;br /&gt;
Сделаем общие выводы о том, как заполняются строки таблицы. Когда будет true в ячейке canMake[items][weight], то есть когда мы можем набрать вес weight первыми items предметами?&lt;br /&gt;
&lt;br /&gt;
* Во-первых, если мы могли набрать вес weight только предыдущими предметами, не используя новый. То есть, если в предыдущей строке на позиции weight было true (canMake[items - 1][weight] == true).&lt;br /&gt;
* Во-вторых, если мы могли набрать вес (weight - currentItemWeight) предыдущими предметами (currentItemWeight — вес текущего предмета). В этом случае, если мы добавим текущий предмет, то мы получим как раз вес weight. То есть, если в предыдущей строке на позиции (weight - currentItemWeight) было true (canMake[items - 1][weight - currentItemWeight] == true).&lt;br /&gt;
&lt;br /&gt;
Теперь мы готовы написать код заполнения таблицы:&lt;br /&gt;
&lt;br /&gt;
 bool solve(const vector&amp;lt;int&amp;gt; &amp;amp;itemWeight, int targetWeight) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; canMake(itemWeight.size() + 1, vector&amp;lt;int&amp;gt;(targetWeight + 1));&lt;br /&gt;
 &lt;br /&gt;
     canMake[0][0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int items = 1; items &amp;lt;= itemWeight.size(); items++) {&lt;br /&gt;
         int currentItemWeight = itemWeight[items - 1];&lt;br /&gt;
 &lt;br /&gt;
         for (int weight = 0; weight &amp;lt;= targetWeight; weight++) {&lt;br /&gt;
             if (canMake[items - 1][weight])&lt;br /&gt;
                 canMake[items][weight] = true;&lt;br /&gt;
 &lt;br /&gt;
             if (weight &amp;gt;= currentItemWeight &amp;amp;&amp;amp; canMake[items - 1][weight - currentItemWeight])&lt;br /&gt;
                 canMake[items][weight] = true;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return canMake[items.size()][targetWeight];&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Ответ на задачу в итоге будет записан в последнюю ячейку последней строки (canMake[itemWeight.size()][targetWeight]).&lt;br /&gt;
&lt;br /&gt;
Временная сложность решения — Θ(NM) (N — количество предметов, M — целевой вес). Для таблицы требуется Θ(NM) памяти.&lt;br /&gt;
&lt;br /&gt;
=== Можно ли набрать заданный вес, каждый предмет можно брать несколько раз ===&lt;br /&gt;
&lt;br /&gt;
Посмотрим, как изменится заполнение таблицы:&lt;br /&gt;
&lt;br /&gt;
       суммарные веса  0  1  2  3  4  5  6  7  8  9 10&lt;br /&gt;
    строка canMake[0]  1  0  0  0  0  0  0  0  0  0  0&lt;br /&gt;
 (предметов пока нет)&lt;br /&gt;
    строка canMake[1]  1  0  0  1  0  0  1  0  0  1  0&lt;br /&gt;
   (вес предмета — 3) &lt;br /&gt;
    строка canMake[2]  1  0  0  1  0  1  1  0  1  1  1&lt;br /&gt;
   (вес предмета — 5)&lt;br /&gt;
    строка canMake[3]  1  0  1  1  1  1  1  1  1  1  1&lt;br /&gt;
   (вес предмета — 2)&lt;br /&gt;
&lt;br /&gt;
canMake[items][weight] == true, если canMake[items - 1][weight] == true или canMake[items][weight - currentItemWeight] == true.&lt;br /&gt;
&lt;br /&gt;
 bool solve(const vector&amp;lt;int&amp;gt; &amp;amp;itemWeight, int targetWeight) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; canMake(itemWeight.size() + 1, vector&amp;lt;int&amp;gt;(targetWeight + 1));&lt;br /&gt;
 &lt;br /&gt;
     canMake[0][0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int items = 1; items &amp;lt;= itemWeight.size(); items++) {&lt;br /&gt;
         int currentItemWeight = itemWeight[items - 1];&lt;br /&gt;
 &lt;br /&gt;
         for (int weight = 0; weight &amp;lt;= targetWeight; weight++) {&lt;br /&gt;
             if (canMake[items - 1][weight])&lt;br /&gt;
                 canMake[items][weight] = true;&lt;br /&gt;
 &lt;br /&gt;
             if (weight &amp;gt;= currentItemWeight &amp;amp;&amp;amp; canMake[{{Changed|items}}][weight - currentItemWeight])&lt;br /&gt;
                 canMake[items][weight] = true;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return canMake[items.size()][targetWeight];&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Временная сложность решения Θ(NM). Для таблицы требуется Θ(NM) памяти.&lt;br /&gt;
&lt;br /&gt;
Заметим, что в данном случае мы можем не делать двумерную таблицу. Нам будет достаточно одной строки canMake[].&lt;br /&gt;
&lt;br /&gt;
 bool solve(const vector&amp;lt;int&amp;gt; &amp;amp;itemWeight, int targetWeight) {&lt;br /&gt;
     vector&amp;lt;bool&amp;gt; canMake(targetWeight + 1);&lt;br /&gt;
 &lt;br /&gt;
     canMake[0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int items = 1; items &amp;lt;= itemWeight.size(); items++) {&lt;br /&gt;
         int currentItemWeight = itemWeight[items - 1];&lt;br /&gt;
 &lt;br /&gt;
         for (int weight = currentItemWeight; weight &amp;lt;= targetWeight; weight++) {&lt;br /&gt;
             if (canMake[weight - currentItemWeight])&lt;br /&gt;
                 canMake[weight] = true;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return canMake[targetWeight];&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Временная сложность решения Θ(NM). Для таблицы требуется Θ(M) памяти.&lt;br /&gt;
&lt;br /&gt;
=== Можно ли набрать заданный вес, Θ(M) памяти ===&lt;br /&gt;
&lt;br /&gt;
Как и в предыдущем случае, мы можем не делать двумерную таблицу. Нам будет достаточно одной строки canMake[].&lt;br /&gt;
&lt;br /&gt;
Здесь, однако, нужно обратить внимание на то, что если мы будем обновлять строку слева направо, то мы будем учитывать один и тот же предмет несколько раз (как в предыдущем случае). То есть, например, если первый предмет имеет вес  3, то мы запишем true в ячейку 3 (так как в ячейке 0 записано true), затем в ячейку 6 (так как в ячейке 3 записано true), затем в ячейку 9 (так как в ячейке 6 записано true)...&lt;br /&gt;
&lt;br /&gt;
Чтобы избежать такой ситуации, следует обновлять строку не слева направо, а справа налево. Тогда в ячейках слева от текущей никогда не будет изменений, связанных с текущим предметом.&lt;br /&gt;
&lt;br /&gt;
 bool solve(const vector&amp;lt;int&amp;gt; &amp;amp;itemWeight, int targetWeight) {&lt;br /&gt;
     vector&amp;lt;bool&amp;gt; canMake(targetWeight + 1);&lt;br /&gt;
 &lt;br /&gt;
     canMake[0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int items = 1; items &amp;lt;= itemWeight.size(); items++) {&lt;br /&gt;
         int currentItemWeight = itemWeight[items - 1];&lt;br /&gt;
 &lt;br /&gt;
         for ({{Changed|1=int weight = targetWeight; weight &amp;gt;= currentItemWeight; weight--}}) {&lt;br /&gt;
             if (canMake[weight - currentItemWeight])&lt;br /&gt;
                 canMake[weight] = true;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return canMake[targetWeight];&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Временная сложность решения Θ(NM). Для таблицы требуется Θ(M) памяти.&lt;br /&gt;
&lt;br /&gt;
=== Можно ли набрать заданный вес, мало предметов, очень большие веса (⋆) ===&lt;br /&gt;
Мы уже не сможем решить такую задачу при помощи динамического программирования, так как при очень большом targetWeight потребуется очень большая таблица, которая не уместится в память (не говоря уже о времени её заполнения).&lt;br /&gt;
&lt;br /&gt;
Если у нас &amp;lt; 25 предметов, можно просто перебрать все их подмножества (рекурсивно или масками, временная сложность O(2^N)).&lt;br /&gt;
&lt;br /&gt;
Если у нас &amp;lt; 50 предметов, кроме перебора понадобится техника meet in the middle. Разделим предметы на две половины. Для каждой половины сделаем set из всех весов, которые можно составить из предметов в этой половине. Дальше нам нужно найти в двух setах два элемента, в сумме дающие targetWeight.&lt;br /&gt;
&lt;br /&gt;
 unordered_set&amp;lt;long long&amp;gt; getTotalWeights(const vector&amp;lt;long long&amp;gt; &amp;amp;itemWeight) {&lt;br /&gt;
     unordered_set&amp;lt;long long&amp;gt; totalWeights;&lt;br /&gt;
 &lt;br /&gt;
     for (int mask = 0; mask &amp;lt; (1 &amp;lt;&amp;lt; itemWeight.size()); mask++) {&lt;br /&gt;
         long long totalWeight = 0;&lt;br /&gt;
 &lt;br /&gt;
         for (int bit = 0; bit &amp;lt; itemWeight.size(); bit++) {&lt;br /&gt;
             if (mask &amp;amp; (1 &amp;lt;&amp;lt; bit)) &lt;br /&gt;
                 totalWeight += itemWeight[bit];&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         totalWeights.push_back(totalWeight);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return totalMasses;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 bool solve(const vector&amp;lt;long long&amp;gt; &amp;amp;itemWeight, long long targetWeight) {&lt;br /&gt;
     auto mid = itemWeight.begin() + itemWeight.size() / 2;&lt;br /&gt;
     unordered_set&amp;lt;long long&amp;gt; aWeights = getTotalWeights({itemWeight.begin(), mid});&lt;br /&gt;
     unordered_set&amp;lt;long long&amp;gt; bWeights = getTotalWeights({mid, itemWeight.end()});&lt;br /&gt;
 &lt;br /&gt;
     for (long long aWeight : aWeights) {&lt;br /&gt;
         if (bWeights.count(targetWeight - aWeight))&lt;br /&gt;
             return true;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Временная сложность решения Θ(2^(N/2)). Для множеств требуется O(2^(N/2)) памяти.&lt;br /&gt;
&lt;br /&gt;
=== Номера предметов, дающих заданный вес ===&lt;br /&gt;
&lt;br /&gt;
Иногда в задаче требуется не только определить, можем ли мы набрать заданный вес, но и вывести сертификат — номера (индексы) предметов, которые мы должны взять.&lt;br /&gt;
&lt;br /&gt;
Вдобавок к таблице canMake[][] сделаем таблицу take[][], в которой будем отмечать, брали ли мы последний предмет. take[items][weight] == true, если, когда мы рассматривали items предметов, мы смогли набрать вес weight, взяв последний предмет (его индекс — (items - 1)).&lt;br /&gt;
&lt;br /&gt;
Заполнение таблицы take[][] происходит одновременно с заполнением таблицы canMake[][]. Всякий раз, когда мы смогли набрать вес при помощи нового предмета, мы ставим true не только в canMake[][], но и в take[][].&lt;br /&gt;
&lt;br /&gt;
 if (weight &amp;gt;= currentItemWeight &amp;amp;&amp;amp; canMake[items - 1][weight - currentItemWeight]) {&lt;br /&gt;
     canMake[items][weight] = true;&lt;br /&gt;
     take[items][weight] = true;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Когда у нас есть заполненная таблица take[][], мы можем по ней восстановить номера использованных предметов. Обозначим за weight вес предметов, которые нам нужно взять (изначально weight == targetWeight).&lt;br /&gt;
&lt;br /&gt;
Как понять, взяли ли мы самый последний предмет? Если take[items][weight] == true, то мы его брали, иначе — не брали. Если мы взяли последний предмет, то weight нужно уменьшить на itemWeight[items - 1].&lt;br /&gt;
&lt;br /&gt;
Как понять, взяли ли мы предпоследний предмет? Если take[items - 1][weight] == true, то мы его брали, иначе — не брали. Если мы взяли предпоследний предмет, то weight нужно уменьшить на itemWeight[items - 2].&lt;br /&gt;
&lt;br /&gt;
И так далее, от конца к началу мы определяем, брали или нет мы каждый из предметов, попутно уменьшая weight.&lt;br /&gt;
&lt;br /&gt;
 {{Changed|vector&amp;lt;int&amp;gt;}} solve(const vector&amp;lt;int&amp;gt; &amp;amp;itemWeight, int targetWeight) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; canMake(itemWeight.size() + 1, vector&amp;lt;int&amp;gt;(targetWeight + 1));&lt;br /&gt;
     {{Changed|vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; take(itemWeight.size() + 1, vector&amp;lt;int&amp;gt;(targetWeight + 1));}}&lt;br /&gt;
 &lt;br /&gt;
     canMake[0][0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int items = 1; items &amp;lt;= itemWeight.size(); items++) {&lt;br /&gt;
         int currentItemWeight = itemWeight[items - 1];&lt;br /&gt;
 &lt;br /&gt;
         for (int weight = 0; weight &amp;lt;= targetWeight; weight++) {&lt;br /&gt;
             if (canMake[items - 1][weight])&lt;br /&gt;
                 canMake[items][weight] = true;&lt;br /&gt;
 &lt;br /&gt;
             if (weight &amp;gt;= currentItemWeight &amp;amp;&amp;amp; canMake[items - 1][weight - currentItemWeight]) {&lt;br /&gt;
                 canMake[items][weight] = true;&lt;br /&gt;
                 {{Changed|1=take[items][weight] = true;}}&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     {{Changed|1=if (!canMake[itemWeight.size()][targetItem])}}&lt;br /&gt;
         {{Changed|1=return {};}}&lt;br /&gt;
 &lt;br /&gt;
     {{Changed|1=vector&amp;lt;int&amp;gt; takenItemIndexes;}}&lt;br /&gt;
     {{Changed|1=for (int items = itemWeight.size(), weight = targetWeight; items &amp;gt; 0; items--) {}}&lt;br /&gt;
         {{Changed|1=if (take[items][weight]) {}}&lt;br /&gt;
             {{Changed|1=takenItemIndexes.push_back(items - 1);}}&lt;br /&gt;
             {{Changed|1=weight -= itemWeight[items - 1];}}&lt;br /&gt;
         {{Changed|1=} }}&lt;br /&gt;
     {{Changed|1=} }}&lt;br /&gt;
     {{Changed|1=return takenItemIndexes;}}&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Временная сложность решения Θ(NM). Для таблиц требуется Θ(NM) памяти.&lt;br /&gt;
&lt;br /&gt;
=== Номера предметов, дающих заданный вес, Θ(M) памяти (⋆) ===&lt;br /&gt;
&lt;br /&gt;
* [https://stackoverflow.com/questions/51941993/0-1-knapsack-find-solution-set-in-space-optimised-implementation stackoverflow.com/questions/51941993/0-1-knapsack-find-solution-set-in-space-optimised-implementation]&lt;br /&gt;
* [https://www.cs.colostate.edu/~cs575dl/Sp2015/Lectures/Knapsack.pdf www.cs.colostate.edu/~cs575dl/Sp2015/Lectures/Knapsack.pdf]&lt;br /&gt;
&lt;br /&gt;
 vector&amp;lt;bool&amp;gt; getCanMake(const vector&amp;lt;int&amp;gt; &amp;amp;itemWeight, int targetWeight, int l, int r) {&lt;br /&gt;
     vector&amp;lt;bool&amp;gt; canMake(targetWeight + 1);&lt;br /&gt;
 &lt;br /&gt;
     canMake[0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemIndex = l; itemIndex &amp;lt;= r; itemIndex++) {&lt;br /&gt;
         int currentItemWeight = itemWeight[itemIndex];&lt;br /&gt;
 &lt;br /&gt;
         for (int weight = currentItemWeight; weight &amp;lt;= targetWeight; weight++) {&lt;br /&gt;
             if (canMake[weight - currentItemWeight])&lt;br /&gt;
                 canMake[weight] = true;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return canMake;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 vector&amp;lt;int&amp;gt; solve(const vector&amp;lt;int&amp;gt; &amp;amp;itemWeight, int targetWeight, int l, int r) {&lt;br /&gt;
     if (l == r)&lt;br /&gt;
         return targetWeight == itemWeight[l] ? { l } : {};&lt;br /&gt;
 &lt;br /&gt;
     int m = l + (r - l) / 2;&lt;br /&gt;
     vector&amp;lt;bool&amp;gt; aCan = getCanMake(itemWeight, targetWeight, l, m);&lt;br /&gt;
     vector&amp;lt;bool&amp;gt; bCan = getCanMake(itemWeight, targetWeight, m + 1, r);&lt;br /&gt;
 &lt;br /&gt;
     int aWeight = 0, bWeight = targetWeight;&lt;br /&gt;
     while (!aCan[aWeight] || !bCan[bWeight]) {&lt;br /&gt;
         aWeight++;&lt;br /&gt;
         bWeight--;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;int&amp;gt; aItemIndexes = solve(itemWeight, aWeight, l, m);&lt;br /&gt;
     vector&amp;lt;int&amp;gt; bItemIndexes = solve(itemWeight, bWeight, m + 1, r);&lt;br /&gt;
     aItemIndexes.insert(aItemIndexes.end(), bIntemIndexes.begin(), bItemIndexes.end());&lt;br /&gt;
     return aItemIndexes;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Временная сложность решения Θ(NM). Для таблицы требуется Θ(M) памяти.&lt;br /&gt;
&lt;br /&gt;
== Вариации на тему задачи о сумме подмножеств ==&lt;br /&gt;
&lt;br /&gt;
=== Какой максимальный вес не больше заданного можно набрать ===&lt;br /&gt;
&lt;br /&gt;
Решаем задачу о сумме подмножеств, ищем самое правое true в последней строке таблицы. &lt;br /&gt;
&lt;br /&gt;
 {{Changed|int}} solve(const vector&amp;lt;int&amp;gt; &amp;amp;itemWeight, int targetWeight) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; canMake(itemWeight.size() + 1, vector&amp;lt;int&amp;gt;(targetWeight + 1));&lt;br /&gt;
 &lt;br /&gt;
     canMake[0][0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int items = 1; items &amp;lt;= itemWeight.size(); items++) {&lt;br /&gt;
         int currentItemWeight = itemWeight[items - 1];&lt;br /&gt;
 &lt;br /&gt;
         for (int weight = 0; weight &amp;lt;= targetWeight; weight++) {&lt;br /&gt;
             if (canMake[items - 1][weight])&lt;br /&gt;
                 canMake[items][weight] = true;&lt;br /&gt;
 &lt;br /&gt;
             if (weight &amp;gt;= currentItemWeight &amp;amp;&amp;amp; canMake[items - 1][weight - currentItemWeight])&lt;br /&gt;
                 canMake[items][weight] = true;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     {{Changed|1=while (!canMake[items.size()][targetWeight])}}&lt;br /&gt;
        {{Changed|1=targetWeight--;}}&lt;br /&gt;
     {{Changed|1=return targetWeight;}}&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Временная сложность решения Θ(NM). Для таблицы требуется Θ(NM) или Θ(M) памяти.&lt;br /&gt;
&lt;br /&gt;
=== Можно ли разделить предметы на два набора одинакового веса ===&lt;br /&gt;
&lt;br /&gt;
Решаем задачу о сумме подмножеств для targetWeight == (сумма весов предметов) / 2. &lt;br /&gt;
&lt;br /&gt;
 bool solve(const vector&amp;lt;int&amp;gt; &amp;amp;itemWeight) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; canMake(itemWeight.size() + 1, vector&amp;lt;int&amp;gt;(targetWeight + 1));&lt;br /&gt;
 &lt;br /&gt;
     {{Changed|1=int targetWeight = 0;}}&lt;br /&gt;
     {{Changed|1=for (int weight : itemWeight)}}&lt;br /&gt;
         {{Changed|1=targetWeight += weight;}}&lt;br /&gt;
     {{Changed|1=targetWeight /= 2;}}&lt;br /&gt;
 &lt;br /&gt;
     canMake[0][0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int items = 1; items &amp;lt;= itemWeight.size(); items++) {&lt;br /&gt;
         int currentItemWeight = itemWeight[items - 1];&lt;br /&gt;
 &lt;br /&gt;
         for (int weight = 0; weight &amp;lt;= targetWeight; weight++) {&lt;br /&gt;
             if (canMake[items - 1][weight])&lt;br /&gt;
                 canMake[items][weight] = true;&lt;br /&gt;
 &lt;br /&gt;
             if (weight &amp;gt;= currentItemWeight &amp;amp;&amp;amp; canMake[items - 1][weight - currentItemWeight])&lt;br /&gt;
                 canMake[items][weight] = true;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return canMake[items.size()][targetWeight];&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Временная сложность решения Θ(NM). Для таблицы требуется Θ(NM) или Θ(M) памяти.&lt;br /&gt;
&lt;br /&gt;
== Примеры кода ==&lt;br /&gt;
&lt;br /&gt;
=== Можно ли набрать сумму targetSum ===&lt;br /&gt;
&lt;br /&gt;
==== O(items.size() * targetSum) памяти ====&lt;br /&gt;
{|&lt;br /&gt;
|width=50%| Предмет каждого вида ровно один&lt;br /&gt;
| &amp;amp;nbsp;&lt;br /&gt;
|width=50%| Предметов каждого вида неограниченно много&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
 bool canMakeSum(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; canMake(items.size() + 1, vector&amp;lt;bool&amp;gt;(targetSum + 1));&lt;br /&gt;
     canMake[0][0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemCount = 1; itemCount &amp;lt;= items.size(); itemCount++) {&lt;br /&gt;
         int itemWeight = items[itemCount - 1];&lt;br /&gt;
         for (int sum = 0; sum &amp;lt;= targetSum; sum++)&lt;br /&gt;
             if (canMake[itemCount - 1][sum] ||&lt;br /&gt;
                 sum &amp;gt;= itemWeight &amp;amp;&amp;amp; canMake[itemCount - 1][sum - itemWeight])&lt;br /&gt;
                 canMake[itemCount][sum] = true;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return canMake[items.size()][targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
 bool canMakeSum(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; canMake(items.size() + 1, vector&amp;lt;bool&amp;gt;(targetSum + 1));&lt;br /&gt;
     canMake[0][0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemCount = 1; itemCount &amp;lt;= items.size(); itemCount++) {&lt;br /&gt;
         int itemWeight = items[itemCount - 1];&lt;br /&gt;
         for (int sum = 0; sum &amp;lt;= targetSum; sum++)&lt;br /&gt;
             if (canMake[itemCount - 1][sum] ||&lt;br /&gt;
                 sum &amp;gt;= itemWeight &amp;amp;&amp;amp; canMake[itemCount][sum - itemWeight])&lt;br /&gt;
                 canMake[itemCount][sum] = true;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return canMake[items.size()][targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== O(items.size() * targetSum) памяти, вернуть сертификат (список предметов) ====&lt;br /&gt;
{|&lt;br /&gt;
|width=50%| Предмет каждого вида ровно один&lt;br /&gt;
| &amp;amp;nbsp;&lt;br /&gt;
|width=50%| Предметов каждого вида неограниченно много&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
 vector&amp;lt;int&amp;gt; canMakeSum(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; canMake(items.size() + 1, vector&amp;lt;bool&amp;gt;(targetSum + 1));&lt;br /&gt;
     canMake[0][0] = true;&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;&amp;gt; from(items.size() + 1, vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;(targetSum + 1));&lt;br /&gt;
 &lt;br /&gt;
     for (int itemCount = 1; itemCount &amp;lt;= items.size(); itemCount++) {&lt;br /&gt;
         int itemWeight = items[itemCount - 1];&lt;br /&gt;
         for (int sum = 0; sum &amp;lt;= targetSum; sum++) {&lt;br /&gt;
             if (canMake[itemCount - 1][sum]) {&lt;br /&gt;
                 canMake[itemCount][sum] = true;&lt;br /&gt;
                 from[itemCount][sum] = { itemCount - 1, sum };&lt;br /&gt;
             } else if (sum &amp;gt;= itemWeight &amp;amp;&amp;amp; canMake[itemCount - 1][sum - itemWeight]) {&lt;br /&gt;
                 canMake[itemCount][sum] = true;&lt;br /&gt;
                 from[itemCount][sum] = { itemCount - 1, sum - itemWeight };&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     if (!canMake[items.size()][targetSum])&lt;br /&gt;
         return {};&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;int&amp;gt; takenItems;&lt;br /&gt;
     for (int itemCount = items.size(), sum = targetSum; itemCount &amp;gt; 0; ) {&lt;br /&gt;
         auto &amp;amp;[prevItemCount, prevSum] = from[itemCount][sum];&lt;br /&gt;
         if (prevSum != sum)&lt;br /&gt;
             takenItems.push_back(sum - prevSum);&lt;br /&gt;
         itemCount = prevItemCount;&lt;br /&gt;
         sum = prevSum;&lt;br /&gt;
     }&lt;br /&gt;
     return takenItems;&lt;br /&gt;
 }&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
 vector&amp;lt;int&amp;gt; canMakeSum(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; canMake(items.size() + 1, vector&amp;lt;bool&amp;gt;(targetSum + 1));&lt;br /&gt;
     canMake[0][0] = true;&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;&amp;gt; from(items.size() + 1, vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;(targetSum + 1));&lt;br /&gt;
 &lt;br /&gt;
     for (int itemCount = 1; itemCount &amp;lt;= items.size(); itemCount++) {&lt;br /&gt;
         int itemWeight = items[itemCount - 1];&lt;br /&gt;
         for (int sum = 0; sum &amp;lt;= targetSum; sum++) {&lt;br /&gt;
             if (canMake[itemCount - 1][sum]) {&lt;br /&gt;
                 canMake[itemCount][sum] = true;&lt;br /&gt;
                 from[itemCount][sum] = { itemCount - 1, sum };&lt;br /&gt;
             } else if (sum &amp;gt;= itemWeight &amp;amp;&amp;amp; canMake[itemCount][sum - itemWeight]) {&lt;br /&gt;
                 canMake[itemCount][sum] = true;&lt;br /&gt;
                 from[itemCount][sum] = { itemCount, sum - itemWeight };&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     if (!canMake[items.size()][targetSum])&lt;br /&gt;
         return {};&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;int&amp;gt; takenItems;&lt;br /&gt;
     for (int itemCount = items.size(), sum = targetSum; itemCount &amp;gt; 0; ) {&lt;br /&gt;
         auto &amp;amp;[prevItemCount, prevSum] = from[itemCount][sum];&lt;br /&gt;
         if (prevSum != sum)&lt;br /&gt;
             takenItems.push_back(sum - prevSum);&lt;br /&gt;
         itemCount = prevItemCount;&lt;br /&gt;
         sum = prevSum;&lt;br /&gt;
     }&lt;br /&gt;
     return takenItems;&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== O(targetSum) памяти ====&lt;br /&gt;
{|&lt;br /&gt;
|width=50%| Предмет каждого вида ровно один&lt;br /&gt;
| &amp;amp;nbsp;&lt;br /&gt;
|width=50%| Предметов каждого вида неограниченно много&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
 bool canMakeSum(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;bool&amp;gt; canMake(targetSum + 1);&lt;br /&gt;
     canMake[0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemWeight : items)&lt;br /&gt;
         for (int sum = targetSum; sum &amp;gt;= itemWeight; sum--)&lt;br /&gt;
             if (canMake[sum - itemWeight])&lt;br /&gt;
                 canMake[sum] = true;&lt;br /&gt;
 &lt;br /&gt;
     return canMake[targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
 bool canMakeSum(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;bool&amp;gt; canMake(targetSum + 1);&lt;br /&gt;
     canMake[0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemWeight : items)&lt;br /&gt;
         for (int sum = itemWeight; sum &amp;lt;= targetSum; sum++)&lt;br /&gt;
             if (canMake[sum - itemWeight])&lt;br /&gt;
                 canMake[sum] = true;&lt;br /&gt;
 &lt;br /&gt;
     return canMake[targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== O(targetSum) памяти, вернуть сертификат (список предметов) ====&lt;br /&gt;
{|&lt;br /&gt;
|width=50%| Предмет каждого вида ровно один&lt;br /&gt;
| &amp;amp;nbsp;&lt;br /&gt;
|width=50%| Предметов каждого вида неограниченно много&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
 vector&amp;lt;int&amp;gt; canMakeSum(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;bool&amp;gt; canMake(targetSum + 1);&lt;br /&gt;
     canMake[0] = true;&lt;br /&gt;
     vector&amp;lt;int&amp;gt; from(targetSum + 1);&lt;br /&gt;
 &lt;br /&gt;
     for (int itemWeight : items) {&lt;br /&gt;
         for (int sum = targetSum; sum &amp;gt;= itemWeight; sum--) {&lt;br /&gt;
             if (!canMake[sum] &amp;amp;&amp;amp; canMake[sum - itemWeight]) {&lt;br /&gt;
                 canMake[sum] = true;&lt;br /&gt;
                 from[sum] = sum - itemWeight;&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     if (!canMake[targetSum])&lt;br /&gt;
         return {};&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;int&amp;gt; takenItems;&lt;br /&gt;
     for (int sum = targetSum; sum &amp;gt; 0; sum = from[sum])&lt;br /&gt;
         takenItems.push_back(sum - from[sum]);&lt;br /&gt;
     return takenItems;&lt;br /&gt;
 }&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
 vector&amp;lt;int&amp;gt; canMakeSum(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;bool&amp;gt; canMake(targetSum + 1);&lt;br /&gt;
     canMake[0] = true;&lt;br /&gt;
     vector&amp;lt;int&amp;gt; from(targetSum + 1);&lt;br /&gt;
 &lt;br /&gt;
     for (int itemWeight : items) {&lt;br /&gt;
         for (int sum = itemWeight; sum &amp;lt;= targetSum; sum++) {&lt;br /&gt;
             if (!canMake[sum] &amp;amp;&amp;amp; canMake[sum - itemWeight]) {&lt;br /&gt;
                 canMake[sum] = true;&lt;br /&gt;
                 from[sum] = sum - itemWeight;&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     if (!canMake[targetSum])&lt;br /&gt;
         return {};&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;int&amp;gt; takenItems;&lt;br /&gt;
     for (int sum = targetSum; sum &amp;gt; 0; sum = from[sum])&lt;br /&gt;
         takenItems.push_back(sum - from[sum]);&lt;br /&gt;
     return takenItems;&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Каким минимальным количеством предметов можно набрать сумму targetSum ===&lt;br /&gt;
&lt;br /&gt;
==== O(items.size() * targetSum) памяти ====&lt;br /&gt;
{|&lt;br /&gt;
|width=50%| Предмет каждого вида ровно один&lt;br /&gt;
| &amp;amp;nbsp;&lt;br /&gt;
|width=50%| Предметов каждого вида неограниченно много&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
 int getMinCount(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; minCount(items.size() + 1, vector&amp;lt;int&amp;gt;(targetSum + 1, 1e9));&lt;br /&gt;
     minCount[0][0] = 0;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemCount = 1; itemCount &amp;lt;= items.size(); itemCount++) {&lt;br /&gt;
         int itemWeight = items[itemCount - 1];&lt;br /&gt;
         for (int sum = 0; sum &amp;lt;= targetSum; sum++) {&lt;br /&gt;
             if (minCount[itemCount][sum] &amp;gt; minCount[itemCount - 1][sum])&lt;br /&gt;
                 minCount[itemCount][sum] = minCount[itemCount - 1][sum];&lt;br /&gt;
             if (sum &amp;gt;= itemWeight &amp;amp;&amp;amp; minCount[itemCount][sum] &amp;gt; minCount[itemCount - 1][sum - itemWeight] + 1)&lt;br /&gt;
                 minCount[itemCount][sum] = minCount[itemCount - 1][sum - itemWeight] + 1;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return minCount[items.size()][targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
 int getMinCount(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; minCount(items.size() + 1, vector&amp;lt;int&amp;gt;(targetSum + 1, 1e9));&lt;br /&gt;
     minCount[0][0] = 0;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemCount = 1; itemCount &amp;lt;= items.size(); itemCount++) {&lt;br /&gt;
         int itemWeight = items[itemCount - 1];&lt;br /&gt;
         for (int sum = 0; sum &amp;lt;= targetSum; sum++) {&lt;br /&gt;
             if (minCount[itemCount][sum] &amp;gt; minCount[itemCount - 1][sum])&lt;br /&gt;
                 minCount[itemCount][sum] = minCount[itemCount - 1][sum];&lt;br /&gt;
             if (sum &amp;gt;= itemWeight &amp;amp;&amp;amp; minCount[itemCount][sum] &amp;gt; minCount[itemCount][sum - itemWeight] + 1)&lt;br /&gt;
                 minCount[itemCount][sum] = minCount[itemCount][sum - itemWeight] + 1;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return minCount[items.size()][targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== O(items.size() * targetSum) памяти, вернуть сертификат (список предметов) ====&lt;br /&gt;
{|&lt;br /&gt;
|width=50%| Предмет каждого вида ровно один&lt;br /&gt;
| &amp;amp;nbsp;&lt;br /&gt;
|width=50%| Предметов каждого вида неограниченно много&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
 vector&amp;lt;int&amp;gt; getMinCount(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; minCount(items.size() + 1, vector&amp;lt;int&amp;gt;(targetSum + 1, 1e9));&lt;br /&gt;
     minCount[0][0] = 0;&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;&amp;gt; from(items.size() + 1, vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;(targetSum + 1));&lt;br /&gt;
 &lt;br /&gt;
     for (int itemCount = 1; itemCount &amp;lt;= items.size(); itemCount++) {&lt;br /&gt;
         int itemWeight = items[itemCount - 1];&lt;br /&gt;
         for (int sum = 0; sum &amp;lt;= targetSum; sum++) {&lt;br /&gt;
             if (minCount[itemCount][sum] &amp;gt; minCount[itemCount - 1][sum]) {&lt;br /&gt;
                 minCount[itemCount][sum] = minCount[itemCount - 1][sum];&lt;br /&gt;
                 from[itemCount][sum] = { itemCount - 1, sum };&lt;br /&gt;
             }&lt;br /&gt;
             if (sum &amp;gt;= itemWeight &amp;amp;&amp;amp; minCount[itemCount][sum] &amp;gt; minCount[itemCount - 1][sum - itemWeight] + 1) {&lt;br /&gt;
                 minCount[itemCount][sum] = minCount[itemCount - 1][sum - itemWeight] + 1;&lt;br /&gt;
                 from[itemCount][sum] = { itemCount - 1, sum - itemWeight };&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     if (minCount[items.size()][targetSum] == 1e9)&lt;br /&gt;
         return {};&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;int&amp;gt; takenItems;&lt;br /&gt;
     for (int itemCount = items.size(), sum = targetSum; itemCount &amp;gt; 0; ) {&lt;br /&gt;
         auto &amp;amp;[prevItemCount, prevSum] = from[itemCount][sum];&lt;br /&gt;
         if (prevSum != sum)&lt;br /&gt;
             takenItems.push_back(sum - prevSum);&lt;br /&gt;
         itemCount = prevItemCount;&lt;br /&gt;
         sum = prevSum;&lt;br /&gt;
     }&lt;br /&gt;
     return takenItems;&lt;br /&gt;
 }&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
 vector&amp;lt;int&amp;gt; getMinCount(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; minCount(items.size() + 1, vector&amp;lt;int&amp;gt;(targetSum + 1, 1e9));&lt;br /&gt;
     minCount[0][0] = 0;&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;&amp;gt; from(items.size() + 1, vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;(targetSum + 1));&lt;br /&gt;
 &lt;br /&gt;
     for (int itemCount = 1; itemCount &amp;lt;= items.size(); itemCount++) {&lt;br /&gt;
         int itemWeight = items[itemCount - 1];&lt;br /&gt;
         for (int sum = 0; sum &amp;lt;= targetSum; sum++) {&lt;br /&gt;
             if (minCount[itemCount][sum] &amp;gt; minCount[itemCount - 1][sum]) {&lt;br /&gt;
                 minCount[itemCount][sum] = minCount[itemCount - 1][sum];&lt;br /&gt;
                 from[itemCount][sum] = { itemCount - 1, sum };&lt;br /&gt;
             }&lt;br /&gt;
             if (sum &amp;gt;= itemWeight &amp;amp;&amp;amp; minCount[itemCount][sum] &amp;gt; minCount[itemCount][sum - itemWeight] + 1) {&lt;br /&gt;
                 minCount[itemCount][sum] = minCount[itemCount][sum - itemWeight] + 1;&lt;br /&gt;
                 from[itemCount][sum] = { itemCount, sum - itemWeight };&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     if (minCount[items.size()][targetSum] == 1e9)&lt;br /&gt;
         return {};&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;int&amp;gt; takenItems;&lt;br /&gt;
     for (int itemCount = items.size(), sum = targetSum; itemCount &amp;gt; 0; ) {&lt;br /&gt;
         auto &amp;amp;[prevItemCount, prevSum] = from[itemCount][sum];&lt;br /&gt;
         if (prevSum != sum)&lt;br /&gt;
             takenItems.push_back(sum - prevSum);&lt;br /&gt;
         itemCount = prevItemCount;&lt;br /&gt;
         sum = prevSum;&lt;br /&gt;
     }&lt;br /&gt;
     return takenItems;&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== O(targetSum) памяти ====&lt;br /&gt;
{|&lt;br /&gt;
|width=50%| Предмет каждого вида ровно один&lt;br /&gt;
| &amp;amp;nbsp;&lt;br /&gt;
|width=50%| Предметов каждого вида неограниченно много&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
 int getMinCount(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;int&amp;gt; minCount(targetSum + 1, 1e9);&lt;br /&gt;
     minCount[0] = 0;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemWeight : items)&lt;br /&gt;
         for (int sum = targetSum; sum &amp;gt;= itemWeight; sum--)&lt;br /&gt;
             if (minCount[sum] &amp;gt; minCount[sum - itemWeight] + 1)&lt;br /&gt;
                 minCount[sum] = minCount[sum - itemWeight] + 1;&lt;br /&gt;
 &lt;br /&gt;
     return minCount[targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
 int getMinCount(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;int&amp;gt; minCount(targetSum + 1, 1e9);&lt;br /&gt;
     minCount[0] = 0;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemWeight : items)&lt;br /&gt;
         for (int sum = itemWeight; sum &amp;lt;= targetSum; sum++)&lt;br /&gt;
             if (minCount[sum] &amp;gt; minCount[sum - itemWeight] + 1)&lt;br /&gt;
                 minCount[sum] = minCount[sum - itemWeight] + 1;&lt;br /&gt;
 &lt;br /&gt;
     return minCount[targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== O(targetSum) памяти, вернуть сертификат (список предметов) ====&lt;br /&gt;
{|&lt;br /&gt;
|width=50%| Предмет каждого вида ровно один&lt;br /&gt;
| &amp;amp;nbsp;&lt;br /&gt;
|width=50%| Предметов каждого вида неограниченно много&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
(Невозможно. Контртест: items = { 1, 2, 3 }, targetSum = 6. Ожидается ответ { 1, 2, 3 }, будет получен ответ { 3, 3 }.)&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
 vector&amp;lt;int&amp;gt; getMinCount(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;int&amp;gt; minCount(targetSum + 1, 1e9);&lt;br /&gt;
     minCount[0] = 0;&lt;br /&gt;
     vector&amp;lt;int&amp;gt; from(targetSum + 1);&lt;br /&gt;
 &lt;br /&gt;
     for (int itemWeight : items) {&lt;br /&gt;
         for (int sum = itemWeight; sum &amp;lt;= targetSum; sum++) {&lt;br /&gt;
             if (minCount[sum] &amp;gt; minCount[sum - itemWeight] + 1) {&lt;br /&gt;
                 minCount[sum] = minCount[sum - itemWeight] + 1;&lt;br /&gt;
                 from[sum] = sum - itemWeight;&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     if (minCount[targetSum] == 1e9)&lt;br /&gt;
         return {};&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;int&amp;gt; takenItems;&lt;br /&gt;
     for (int sum = targetSum; sum &amp;gt; 0; sum = from[sum])&lt;br /&gt;
         takenItems.push_back(sum - from[sum]);&lt;br /&gt;
     return takenItems;&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Сколько есть способов набрать сумму targetSum (без учёта порядка, 1 + 2 == 2 + 1) ===&lt;br /&gt;
&lt;br /&gt;
==== O(items.size() * targetSum) памяти ====&lt;br /&gt;
{|&lt;br /&gt;
|width=50%| Предмет каждого вида ровно один&lt;br /&gt;
| &amp;amp;nbsp;&lt;br /&gt;
|width=50%| Предметов каждого вида неограниченно много&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
 long long getWays(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;long long&amp;gt;&amp;gt; ways(items.size() + 1, vector&amp;lt;long long&amp;gt;(targetSum + 1));&lt;br /&gt;
     ways[0][0] = 1;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemCount = 1; itemCount &amp;lt;= items.size(); itemCount++) {&lt;br /&gt;
         int itemWeight = items[itemCount - 1];&lt;br /&gt;
         for (int sum = 0; sum &amp;lt;= targetSum; sum++) {&lt;br /&gt;
             ways[itemCount][sum] += ways[itemCount - 1][sum];&lt;br /&gt;
             if (sum &amp;gt;= itemWeight)&lt;br /&gt;
                 ways[itemCount][sum] += ways[itemCount - 1][sum - itemWeight];&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return ways[items.size()][targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
 long long getWays(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;long long&amp;gt;&amp;gt; ways(items.size() + 1, vector&amp;lt;long long&amp;gt;(targetSum + 1));&lt;br /&gt;
     ways[0][0] = 1;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemCount = 1; itemCount &amp;lt;= items.size(); itemCount++) {&lt;br /&gt;
         int itemWeight = items[itemCount - 1];&lt;br /&gt;
         for (int sum = 0; sum &amp;lt;= targetSum; sum++) {&lt;br /&gt;
             ways[itemCount][sum] += ways[itemCount - 1][sum];&lt;br /&gt;
             if (sum &amp;gt;= itemWeight)&lt;br /&gt;
                 ways[itemCount][sum] += ways[itemCount][sum - itemWeight];&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return ways[items.size()][targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== O(targetSum) памяти ====&lt;br /&gt;
{|&lt;br /&gt;
|width=50%| Предмет каждого вида ровно один&lt;br /&gt;
| &amp;amp;nbsp;&lt;br /&gt;
|width=50%| Предметов каждого вида неограниченно много&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
 long long getWays(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;long long&amp;gt; ways(targetSum + 1);&lt;br /&gt;
     ways[0] = 1;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemWeight : items)&lt;br /&gt;
         for (int sum = targetSum; sum &amp;gt;= itemWeight; sum--)&lt;br /&gt;
             ways[sum] += ways[sum - itemWeight];&lt;br /&gt;
 &lt;br /&gt;
     return ways[targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
 long long getWays(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;long long&amp;gt; ways(targetSum + 1);&lt;br /&gt;
     ways[0] = 1;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemWeight : items)&lt;br /&gt;
         for (int sum = itemWeight; sum &amp;lt;= targetSum; sum++)&lt;br /&gt;
             ways[sum] += ways[sum - itemWeight];&lt;br /&gt;
 &lt;br /&gt;
     return ways[targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Сколько есть способов набрать сумму targetSum (с учётом порядка, 1 + 2 != 2 + 1) ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|width=50%| Предмет каждого вида ровно один&lt;br /&gt;
| &amp;amp;nbsp;&lt;br /&gt;
|width=50%| Предметов каждого вида неограниченно много&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
(O(targetSum * items.size()) памяти.)&lt;br /&gt;
 long long getOrderedWays(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;long long&amp;gt;&amp;gt; ways(targetSum + 1, vector&amp;lt;long long&amp;gt;(items.size() + 1));&lt;br /&gt;
     ways[0][0] = 1;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemWeight : items)&lt;br /&gt;
         for (int sum = targetSum; sum &amp;gt;= itemWeight; sum--)&lt;br /&gt;
             for (int takenItemCount = 1; takenItemCount &amp;lt;= items.size(); takenItemCount++)&lt;br /&gt;
                 ways[sum][takenItemCount] += ways[sum - itemWeight][takenItemCount - 1] * takenItemCount;&lt;br /&gt;
 &lt;br /&gt;
     long long res = 0;&lt;br /&gt;
     for (int takenItemCount = 0; takenItemCount &amp;lt;= items.size(); takenItemCount++)&lt;br /&gt;
         res += ways[targetSum][takenItemCount];&lt;br /&gt;
     return res;&lt;br /&gt;
 }&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
(O(targetSum) памяти. По сути, это другая формулировка задачи о кузнечике.)&lt;br /&gt;
 long long getOrderedWays(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;long long&amp;gt; ways(targetSum + 1);&lt;br /&gt;
     ways[0] = 1;&lt;br /&gt;
 &lt;br /&gt;
     for (int sum = 1; sum &amp;lt;= targetSum; sum++)&lt;br /&gt;
         for (int itemWeight : items)&lt;br /&gt;
             if (sum &amp;gt;= itemWeight)&lt;br /&gt;
                 ways[sum] += ways[sum - itemWeight];&lt;br /&gt;
 &lt;br /&gt;
     return ways[targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
* [http://algorithmica.org/tg/knapsack-gis-gcs algorithmica.org — Задача о рюкзаке, НВП и НОП]&lt;br /&gt;
* [https://brilliant.org/wiki/backpack-problem/ brilliant.org — Backpack Problem]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Динамическое программирование]]&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=%D0%97%D0%B0%D0%B4%D0%B0%D1%87%D0%B0_%D0%BE_%D1%80%D1%8E%D0%BA%D0%B7%D0%B0%D0%BA%D0%B5_%D0%B8_%D1%81%D0%B2%D1%8F%D0%B7%D0%B0%D0%BD%D0%BD%D1%8B%D0%B5_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8&amp;diff=2937</id>
		<title>Задача о рюкзаке и связанные задачи</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=%D0%97%D0%B0%D0%B4%D0%B0%D1%87%D0%B0_%D0%BE_%D1%80%D1%8E%D0%BA%D0%B7%D0%B0%D0%BA%D0%B5_%D0%B8_%D1%81%D0%B2%D1%8F%D0%B7%D0%B0%D0%BD%D0%BD%D1%8B%D0%B5_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8&amp;diff=2937"/>
		<updated>2025-11-19T00:35:18Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: /* Примеры кода */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__TOC__ &lt;br /&gt;
&lt;br /&gt;
== Задача о сумме подмножеств (subset sum problem) ==&lt;br /&gt;
&lt;br /&gt;
=== Можно ли набрать заданный вес ===&lt;br /&gt;
Есть несколько предметов, для каждого известен вес. Можем ли мы выбрать несколько предметов так, чтобы общий вес был в точности равен заданному числу?&lt;br /&gt;
&lt;br /&gt;
 vector&amp;lt;int&amp;gt; itemWeight; // веса предметов&lt;br /&gt;
 int targetWeight;       // вес, который нужно набрать&lt;br /&gt;
&lt;br /&gt;
Будем заполнять таблицу canMake[][]. Строки таблицы соответствуют количеству рассмотренных предметов: нулевая строка — ноль предметов, первая строка — один предмет, ..., N-я строка — N предметов. Столбцы таблицы соответствуют весам от 0 до заданного веса. В ячейках хранятся true и false: canMake[items][weight] == true, если мы можем набрать вес weight, используя некоторые из items первых предметов.&lt;br /&gt;
&lt;br /&gt;
 vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; canMake(itemWeight.size() + 1, vector&amp;lt;int&amp;gt;(targetWeight + 1));&lt;br /&gt;
&lt;br /&gt;
Как должна выглядеть нулевая строка таблицы? В нулевой строке мы описываем ситуацию, когда не рассмотрено ещё ни одного предмета; какие веса мы при этом можем набрать? Очевидно, только вес 0. Поэтому ячейка canMake[0][0] должна содержать true, а остальные ячейки первой строки — false.&lt;br /&gt;
&lt;br /&gt;
       суммарные веса  0  1  2  3  4  5  6  7  8  9 10&lt;br /&gt;
    строка canMake[0]  1  0  0  0  0  0  0  0  0  0  0 &lt;br /&gt;
 (предметов пока нет)&lt;br /&gt;
&lt;br /&gt;
Теперь разберёмся, как должна выглядеть первая строка таблицы (она соответствует первому предмету). Пусть, для определённости, вес первого предмета — 3 (itemWeight[0] == 3). Какие веса мы теперь можем набрать?&lt;br /&gt;
&lt;br /&gt;
Нам всё ещё доступен вес 0 (если мы не будем брать первый предмет), но теперь мы можем получить ещё и вес 3 (если мы возьмём первый предмет).&lt;br /&gt;
&lt;br /&gt;
       суммарные веса  0  1  2  3  4  5  6  7  8  9 10&lt;br /&gt;
    строка canMake[0]  1  0  0  0  0  0  0  0  0  0  0&lt;br /&gt;
 (предметов пока нет)&lt;br /&gt;
    строка canMake[1]  1  0  0  1  0  0  0  0  0  0  0&lt;br /&gt;
   (вес предмета — 3) &lt;br /&gt;
&lt;br /&gt;
Далее — вторая строка таблицы (она соответствует первым двум предметам). Пусть вес второго предмета — 5 (itemWeight[1] == 5). Какие веса мы теперь можем набрать?&lt;br /&gt;
&lt;br /&gt;
Нам всё ещё доступны веса 0 и 3 (если мы не будем брать второй предмет), но теперь мы можем получить ещё веса 5 (если мы до этого не брали ничего, а теперь возьмём второй предмет) и 8 (если мы до этого взяли первый предмет и получили вес 3, а сейчас добавим второй предмет).&lt;br /&gt;
&lt;br /&gt;
       суммарные веса  0  1  2  3  4  5  6  7  8  9 10&lt;br /&gt;
    строка canMake[0]  1  0  0  0  0  0  0  0  0  0  0&lt;br /&gt;
 (предметов пока нет)&lt;br /&gt;
    строка canMake[1]  1  0  0  1  0  0  0  0  0  0  0&lt;br /&gt;
   (вес предмета — 3) &lt;br /&gt;
    строка canMake[2]  1  0  0  1  0  1  0  0  1  0  0&lt;br /&gt;
   (вес предмета — 5)&lt;br /&gt;
&lt;br /&gt;
Далее — третья строка таблицы (она соответствует первым трём предметам). Пусть вес третьего предмета — 2 (itemWeight[2] == 2).&lt;br /&gt;
&lt;br /&gt;
Раньше (без третьего предмета) мы могли набрать веса 0, 3, 5 и 8. Теперь (с третьим предметом) можем дополнительно набрать 2, 5, 7 и 10. Обратите внимание, что вес 5 мы могли набрать как без третьего предмета (просто взяв второй предмет с весом 5), так и с ним (взяв первый и третий предметы с весами 3 и 2); ничего необычного в этой ситуации нет.&lt;br /&gt;
&lt;br /&gt;
       суммарные веса  0  1  2  3  4  5  6  7  8  9 10&lt;br /&gt;
    строка canMake[0]  1  0  0  0  0  0  0  0  0  0  0&lt;br /&gt;
 (предметов пока нет)&lt;br /&gt;
    строка canMake[1]  1  0  0  1  0  0  0  0  0  0  0&lt;br /&gt;
   (вес предмета — 3) &lt;br /&gt;
    строка canMake[2]  1  0  0  1  0  1  0  0  1  0  0&lt;br /&gt;
   (вес предмета — 5)&lt;br /&gt;
    строка canMake[3]  1  0  1  1  0  1  0  1  1  0  1&lt;br /&gt;
   (вес предмета — 2)&lt;br /&gt;
&lt;br /&gt;
Сделаем общие выводы о том, как заполняются строки таблицы. Когда будет true в ячейке canMake[items][weight], то есть когда мы можем набрать вес weight первыми items предметами?&lt;br /&gt;
&lt;br /&gt;
* Во-первых, если мы могли набрать вес weight только предыдущими предметами, не используя новый. То есть, если в предыдущей строке на позиции weight было true (canMake[items - 1][weight] == true).&lt;br /&gt;
* Во-вторых, если мы могли набрать вес (weight - currentItemWeight) предыдущими предметами (currentItemWeight — вес текущего предмета). В этом случае, если мы добавим текущий предмет, то мы получим как раз вес weight. То есть, если в предыдущей строке на позиции (weight - currentItemWeight) было true (canMake[items - 1][weight - currentItemWeight] == true).&lt;br /&gt;
&lt;br /&gt;
Теперь мы готовы написать код заполнения таблицы:&lt;br /&gt;
&lt;br /&gt;
 bool solve(const vector&amp;lt;int&amp;gt; &amp;amp;itemWeight, int targetWeight) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; canMake(itemWeight.size() + 1, vector&amp;lt;int&amp;gt;(targetWeight + 1));&lt;br /&gt;
 &lt;br /&gt;
     canMake[0][0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int items = 1; items &amp;lt;= itemWeight.size(); items++) {&lt;br /&gt;
         int currentItemWeight = itemWeight[items - 1];&lt;br /&gt;
 &lt;br /&gt;
         for (int weight = 0; weight &amp;lt;= targetWeight; weight++) {&lt;br /&gt;
             if (canMake[items - 1][weight])&lt;br /&gt;
                 canMake[items][weight] = true;&lt;br /&gt;
 &lt;br /&gt;
             if (weight &amp;gt;= currentItemWeight &amp;amp;&amp;amp; canMake[items - 1][weight - currentItemWeight])&lt;br /&gt;
                 canMake[items][weight] = true;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return canMake[items.size()][targetWeight];&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Ответ на задачу в итоге будет записан в последнюю ячейку последней строки (canMake[itemWeight.size()][targetWeight]).&lt;br /&gt;
&lt;br /&gt;
Временная сложность решения — Θ(NM) (N — количество предметов, M — целевой вес). Для таблицы требуется Θ(NM) памяти.&lt;br /&gt;
&lt;br /&gt;
=== Можно ли набрать заданный вес, каждый предмет можно брать несколько раз ===&lt;br /&gt;
&lt;br /&gt;
Посмотрим, как изменится заполнение таблицы:&lt;br /&gt;
&lt;br /&gt;
       суммарные веса  0  1  2  3  4  5  6  7  8  9 10&lt;br /&gt;
    строка canMake[0]  1  0  0  0  0  0  0  0  0  0  0&lt;br /&gt;
 (предметов пока нет)&lt;br /&gt;
    строка canMake[1]  1  0  0  1  0  0  1  0  0  1  0&lt;br /&gt;
   (вес предмета — 3) &lt;br /&gt;
    строка canMake[2]  1  0  0  1  0  1  1  0  1  1  1&lt;br /&gt;
   (вес предмета — 5)&lt;br /&gt;
    строка canMake[3]  1  0  1  1  1  1  1  1  1  1  1&lt;br /&gt;
   (вес предмета — 2)&lt;br /&gt;
&lt;br /&gt;
canMake[items][weight] == true, если canMake[items - 1][weight] == true или canMake[items][weight - currentItemWeight] == true.&lt;br /&gt;
&lt;br /&gt;
 bool solve(const vector&amp;lt;int&amp;gt; &amp;amp;itemWeight, int targetWeight) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; canMake(itemWeight.size() + 1, vector&amp;lt;int&amp;gt;(targetWeight + 1));&lt;br /&gt;
 &lt;br /&gt;
     canMake[0][0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int items = 1; items &amp;lt;= itemWeight.size(); items++) {&lt;br /&gt;
         int currentItemWeight = itemWeight[items - 1];&lt;br /&gt;
 &lt;br /&gt;
         for (int weight = 0; weight &amp;lt;= targetWeight; weight++) {&lt;br /&gt;
             if (canMake[items - 1][weight])&lt;br /&gt;
                 canMake[items][weight] = true;&lt;br /&gt;
 &lt;br /&gt;
             if (weight &amp;gt;= currentItemWeight &amp;amp;&amp;amp; canMake[{{Changed|items}}][weight - currentItemWeight])&lt;br /&gt;
                 canMake[items][weight] = true;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return canMake[items.size()][targetWeight];&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Временная сложность решения Θ(NM). Для таблицы требуется Θ(NM) памяти.&lt;br /&gt;
&lt;br /&gt;
Заметим, что в данном случае мы можем не делать двумерную таблицу. Нам будет достаточно одной строки canMake[].&lt;br /&gt;
&lt;br /&gt;
 bool solve(const vector&amp;lt;int&amp;gt; &amp;amp;itemWeight, int targetWeight) {&lt;br /&gt;
     vector&amp;lt;bool&amp;gt; canMake(targetWeight + 1);&lt;br /&gt;
 &lt;br /&gt;
     canMake[0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int items = 1; items &amp;lt;= itemWeight.size(); items++) {&lt;br /&gt;
         int currentItemWeight = itemWeight[items - 1];&lt;br /&gt;
 &lt;br /&gt;
         for (int weight = currentItemWeight; weight &amp;lt;= targetWeight; weight++) {&lt;br /&gt;
             if (canMake[weight - currentItemWeight])&lt;br /&gt;
                 canMake[weight] = true;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return canMake[targetWeight];&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Временная сложность решения Θ(NM). Для таблицы требуется Θ(M) памяти.&lt;br /&gt;
&lt;br /&gt;
=== Можно ли набрать заданный вес, Θ(M) памяти ===&lt;br /&gt;
&lt;br /&gt;
Как и в предыдущем случае, мы можем не делать двумерную таблицу. Нам будет достаточно одной строки canMake[].&lt;br /&gt;
&lt;br /&gt;
Здесь, однако, нужно обратить внимание на то, что если мы будем обновлять строку слева направо, то мы будем учитывать один и тот же предмет несколько раз (как в предыдущем случае). То есть, например, если первый предмет имеет вес  3, то мы запишем true в ячейку 3 (так как в ячейке 0 записано true), затем в ячейку 6 (так как в ячейке 3 записано true), затем в ячейку 9 (так как в ячейке 6 записано true)...&lt;br /&gt;
&lt;br /&gt;
Чтобы избежать такой ситуации, следует обновлять строку не слева направо, а справа налево. Тогда в ячейках слева от текущей никогда не будет изменений, связанных с текущим предметом.&lt;br /&gt;
&lt;br /&gt;
 bool solve(const vector&amp;lt;int&amp;gt; &amp;amp;itemWeight, int targetWeight) {&lt;br /&gt;
     vector&amp;lt;bool&amp;gt; canMake(targetWeight + 1);&lt;br /&gt;
 &lt;br /&gt;
     canMake[0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int items = 1; items &amp;lt;= itemWeight.size(); items++) {&lt;br /&gt;
         int currentItemWeight = itemWeight[items - 1];&lt;br /&gt;
 &lt;br /&gt;
         for ({{Changed|1=int weight = targetWeight; weight &amp;gt;= currentItemWeight; weight--}}) {&lt;br /&gt;
             if (canMake[weight - currentItemWeight])&lt;br /&gt;
                 canMake[weight] = true;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return canMake[targetWeight];&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Временная сложность решения Θ(NM). Для таблицы требуется Θ(M) памяти.&lt;br /&gt;
&lt;br /&gt;
=== Можно ли набрать заданный вес, мало предметов, очень большие веса (⋆) ===&lt;br /&gt;
Мы уже не сможем решить такую задачу при помощи динамического программирования, так как при очень большом targetWeight потребуется очень большая таблица, которая не уместится в память (не говоря уже о времени её заполнения).&lt;br /&gt;
&lt;br /&gt;
Если у нас &amp;lt; 25 предметов, можно просто перебрать все их подмножества (рекурсивно или масками, временная сложность O(2^N)).&lt;br /&gt;
&lt;br /&gt;
Если у нас &amp;lt; 50 предметов, кроме перебора понадобится техника meet in the middle. Разделим предметы на две половины. Для каждой половины сделаем set из всех весов, которые можно составить из предметов в этой половине. Дальше нам нужно найти в двух setах два элемента, в сумме дающие targetWeight.&lt;br /&gt;
&lt;br /&gt;
 unordered_set&amp;lt;long long&amp;gt; getTotalWeights(const vector&amp;lt;long long&amp;gt; &amp;amp;itemWeight) {&lt;br /&gt;
     unordered_set&amp;lt;long long&amp;gt; totalWeights;&lt;br /&gt;
 &lt;br /&gt;
     for (int mask = 0; mask &amp;lt; (1 &amp;lt;&amp;lt; itemWeight.size()); mask++) {&lt;br /&gt;
         long long totalWeight = 0;&lt;br /&gt;
 &lt;br /&gt;
         for (int bit = 0; bit &amp;lt; itemWeight.size(); bit++) {&lt;br /&gt;
             if (mask &amp;amp; (1 &amp;lt;&amp;lt; bit)) &lt;br /&gt;
                 totalWeight += itemWeight[bit];&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         totalWeights.push_back(totalWeight);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return totalMasses;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 bool solve(const vector&amp;lt;long long&amp;gt; &amp;amp;itemWeight, long long targetWeight) {&lt;br /&gt;
     auto mid = itemWeight.begin() + itemWeight.size() / 2;&lt;br /&gt;
     unordered_set&amp;lt;long long&amp;gt; aWeights = getTotalWeights({itemWeight.begin(), mid});&lt;br /&gt;
     unordered_set&amp;lt;long long&amp;gt; bWeights = getTotalWeights({mid, itemWeight.end()});&lt;br /&gt;
 &lt;br /&gt;
     for (long long aWeight : aWeights) {&lt;br /&gt;
         if (bWeights.count(targetWeight - aWeight))&lt;br /&gt;
             return true;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Временная сложность решения Θ(2^(N/2)). Для множеств требуется O(2^(N/2)) памяти.&lt;br /&gt;
&lt;br /&gt;
=== Номера предметов, дающих заданный вес ===&lt;br /&gt;
&lt;br /&gt;
Иногда в задаче требуется не только определить, можем ли мы набрать заданный вес, но и вывести сертификат — номера (индексы) предметов, которые мы должны взять.&lt;br /&gt;
&lt;br /&gt;
Вдобавок к таблице canMake[][] сделаем таблицу take[][], в которой будем отмечать, брали ли мы последний предмет. take[items][weight] == true, если, когда мы рассматривали items предметов, мы смогли набрать вес weight, взяв последний предмет (его индекс — (items - 1)).&lt;br /&gt;
&lt;br /&gt;
Заполнение таблицы take[][] происходит одновременно с заполнением таблицы canMake[][]. Всякий раз, когда мы смогли набрать вес при помощи нового предмета, мы ставим true не только в canMake[][], но и в take[][].&lt;br /&gt;
&lt;br /&gt;
 if (weight &amp;gt;= currentItemWeight &amp;amp;&amp;amp; canMake[items - 1][weight - currentItemWeight]) {&lt;br /&gt;
     canMake[items][weight] = true;&lt;br /&gt;
     take[items][weight] = true;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Когда у нас есть заполненная таблица take[][], мы можем по ней восстановить номера использованных предметов. Обозначим за weight вес предметов, которые нам нужно взять (изначально weight == targetWeight).&lt;br /&gt;
&lt;br /&gt;
Как понять, взяли ли мы самый последний предмет? Если take[items][weight] == true, то мы его брали, иначе — не брали. Если мы взяли последний предмет, то weight нужно уменьшить на itemWeight[items - 1].&lt;br /&gt;
&lt;br /&gt;
Как понять, взяли ли мы предпоследний предмет? Если take[items - 1][weight] == true, то мы его брали, иначе — не брали. Если мы взяли предпоследний предмет, то weight нужно уменьшить на itemWeight[items - 2].&lt;br /&gt;
&lt;br /&gt;
И так далее, от конца к началу мы определяем, брали или нет мы каждый из предметов, попутно уменьшая weight.&lt;br /&gt;
&lt;br /&gt;
 {{Changed|vector&amp;lt;int&amp;gt;}} solve(const vector&amp;lt;int&amp;gt; &amp;amp;itemWeight, int targetWeight) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; canMake(itemWeight.size() + 1, vector&amp;lt;int&amp;gt;(targetWeight + 1));&lt;br /&gt;
     {{Changed|vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; take(itemWeight.size() + 1, vector&amp;lt;int&amp;gt;(targetWeight + 1));}}&lt;br /&gt;
 &lt;br /&gt;
     canMake[0][0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int items = 1; items &amp;lt;= itemWeight.size(); items++) {&lt;br /&gt;
         int currentItemWeight = itemWeight[items - 1];&lt;br /&gt;
 &lt;br /&gt;
         for (int weight = 0; weight &amp;lt;= targetWeight; weight++) {&lt;br /&gt;
             if (canMake[items - 1][weight])&lt;br /&gt;
                 canMake[items][weight] = true;&lt;br /&gt;
 &lt;br /&gt;
             if (weight &amp;gt;= currentItemWeight &amp;amp;&amp;amp; canMake[items - 1][weight - currentItemWeight]) {&lt;br /&gt;
                 canMake[items][weight] = true;&lt;br /&gt;
                 {{Changed|1=take[items][weight] = true;}}&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     {{Changed|1=if (!canMake[itemWeight.size()][targetItem])}}&lt;br /&gt;
         {{Changed|1=return {};}}&lt;br /&gt;
 &lt;br /&gt;
     {{Changed|1=vector&amp;lt;int&amp;gt; takenItemIndexes;}}&lt;br /&gt;
     {{Changed|1=for (int items = itemWeight.size(), weight = targetWeight; items &amp;gt; 0; items--) {}}&lt;br /&gt;
         {{Changed|1=if (take[items][weight]) {}}&lt;br /&gt;
             {{Changed|1=takenItemIndexes.push_back(items - 1);}}&lt;br /&gt;
             {{Changed|1=weight -= itemWeight[items - 1];}}&lt;br /&gt;
         {{Changed|1=} }}&lt;br /&gt;
     {{Changed|1=} }}&lt;br /&gt;
     {{Changed|1=return takenItemIndexes;}}&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Временная сложность решения Θ(NM). Для таблиц требуется Θ(NM) памяти.&lt;br /&gt;
&lt;br /&gt;
=== Номера предметов, дающих заданный вес, Θ(M) памяти (⋆) ===&lt;br /&gt;
&lt;br /&gt;
* [https://stackoverflow.com/questions/51941993/0-1-knapsack-find-solution-set-in-space-optimised-implementation stackoverflow.com/questions/51941993/0-1-knapsack-find-solution-set-in-space-optimised-implementation]&lt;br /&gt;
* [https://www.cs.colostate.edu/~cs575dl/Sp2015/Lectures/Knapsack.pdf www.cs.colostate.edu/~cs575dl/Sp2015/Lectures/Knapsack.pdf]&lt;br /&gt;
&lt;br /&gt;
 vector&amp;lt;bool&amp;gt; getCanMake(const vector&amp;lt;int&amp;gt; &amp;amp;itemWeight, int targetWeight, int l, int r) {&lt;br /&gt;
     vector&amp;lt;bool&amp;gt; canMake(targetWeight + 1);&lt;br /&gt;
 &lt;br /&gt;
     canMake[0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemIndex = l; itemIndex &amp;lt;= r; itemIndex++) {&lt;br /&gt;
         int currentItemWeight = itemWeight[itemIndex];&lt;br /&gt;
 &lt;br /&gt;
         for (int weight = currentItemWeight; weight &amp;lt;= targetWeight; weight++) {&lt;br /&gt;
             if (canMake[weight - currentItemWeight])&lt;br /&gt;
                 canMake[weight] = true;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return canMake;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 vector&amp;lt;int&amp;gt; solve(const vector&amp;lt;int&amp;gt; &amp;amp;itemWeight, int targetWeight, int l, int r) {&lt;br /&gt;
     if (l == r)&lt;br /&gt;
         return targetWeight == itemWeight[l] ? { l } : {};&lt;br /&gt;
 &lt;br /&gt;
     int m = l + (r - l) / 2;&lt;br /&gt;
     vector&amp;lt;bool&amp;gt; aCan = getCanMake(itemWeight, targetWeight, l, m);&lt;br /&gt;
     vector&amp;lt;bool&amp;gt; bCan = getCanMake(itemWeight, targetWeight, m + 1, r);&lt;br /&gt;
 &lt;br /&gt;
     int aWeight = 0, bWeight = targetWeight;&lt;br /&gt;
     while (!aCan[aWeight] || !bCan[bWeight]) {&lt;br /&gt;
         aWeight++;&lt;br /&gt;
         bWeight--;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;int&amp;gt; aItemIndexes = solve(itemWeight, aWeight, l, m);&lt;br /&gt;
     vector&amp;lt;int&amp;gt; bItemIndexes = solve(itemWeight, bWeight, m + 1, r);&lt;br /&gt;
     aItemIndexes.insert(aItemIndexes.end(), bIntemIndexes.begin(), bItemIndexes.end());&lt;br /&gt;
     return aItemIndexes;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Временная сложность решения Θ(NM). Для таблицы требуется Θ(M) памяти.&lt;br /&gt;
&lt;br /&gt;
== Вариации на тему задачи о сумме подмножеств ==&lt;br /&gt;
&lt;br /&gt;
=== Какой максимальный вес не больше заданного можно набрать ===&lt;br /&gt;
&lt;br /&gt;
Решаем задачу о сумме подмножеств, ищем самое правое true в последней строке таблицы. &lt;br /&gt;
&lt;br /&gt;
 {{Changed|int}} solve(const vector&amp;lt;int&amp;gt; &amp;amp;itemWeight, int targetWeight) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; canMake(itemWeight.size() + 1, vector&amp;lt;int&amp;gt;(targetWeight + 1));&lt;br /&gt;
 &lt;br /&gt;
     canMake[0][0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int items = 1; items &amp;lt;= itemWeight.size(); items++) {&lt;br /&gt;
         int currentItemWeight = itemWeight[items - 1];&lt;br /&gt;
 &lt;br /&gt;
         for (int weight = 0; weight &amp;lt;= targetWeight; weight++) {&lt;br /&gt;
             if (canMake[items - 1][weight])&lt;br /&gt;
                 canMake[items][weight] = true;&lt;br /&gt;
 &lt;br /&gt;
             if (weight &amp;gt;= currentItemWeight &amp;amp;&amp;amp; canMake[items - 1][weight - currentItemWeight])&lt;br /&gt;
                 canMake[items][weight] = true;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     {{Changed|1=while (!canMake[items.size()][targetWeight])}}&lt;br /&gt;
        {{Changed|1=targetWeight--;}}&lt;br /&gt;
     {{Changed|1=return targetWeight;}}&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Временная сложность решения Θ(NM). Для таблицы требуется Θ(NM) или Θ(M) памяти.&lt;br /&gt;
&lt;br /&gt;
=== Можно ли разделить предметы на два набора одинакового веса ===&lt;br /&gt;
&lt;br /&gt;
Решаем задачу о сумме подмножеств для targetWeight == (сумма весов предметов) / 2. &lt;br /&gt;
&lt;br /&gt;
 bool solve(const vector&amp;lt;int&amp;gt; &amp;amp;itemWeight) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; canMake(itemWeight.size() + 1, vector&amp;lt;int&amp;gt;(targetWeight + 1));&lt;br /&gt;
 &lt;br /&gt;
     {{Changed|1=int targetWeight = 0;}}&lt;br /&gt;
     {{Changed|1=for (int weight : itemWeight)}}&lt;br /&gt;
         {{Changed|1=targetWeight += weight;}}&lt;br /&gt;
     {{Changed|1=targetWeight /= 2;}}&lt;br /&gt;
 &lt;br /&gt;
     canMake[0][0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int items = 1; items &amp;lt;= itemWeight.size(); items++) {&lt;br /&gt;
         int currentItemWeight = itemWeight[items - 1];&lt;br /&gt;
 &lt;br /&gt;
         for (int weight = 0; weight &amp;lt;= targetWeight; weight++) {&lt;br /&gt;
             if (canMake[items - 1][weight])&lt;br /&gt;
                 canMake[items][weight] = true;&lt;br /&gt;
 &lt;br /&gt;
             if (weight &amp;gt;= currentItemWeight &amp;amp;&amp;amp; canMake[items - 1][weight - currentItemWeight])&lt;br /&gt;
                 canMake[items][weight] = true;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return canMake[items.size()][targetWeight];&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Временная сложность решения Θ(NM). Для таблицы требуется Θ(NM) или Θ(M) памяти.&lt;br /&gt;
&lt;br /&gt;
== Примеры кода ==&lt;br /&gt;
&lt;br /&gt;
=== Можно ли набрать сумму targetSum ===&lt;br /&gt;
&lt;br /&gt;
==== O(items.size() * targetSum) памяти ====&lt;br /&gt;
{|&lt;br /&gt;
|width=50%| Предмет каждого вида ровно один&lt;br /&gt;
| &amp;amp;nbsp;&lt;br /&gt;
|width=50%| Предметов каждого вида неограниченно много&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
 bool canMakeSum(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; canMake(items.size() + 1, vector&amp;lt;bool&amp;gt;(targetSum + 1));&lt;br /&gt;
     canMake[0][0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemCount = 1; itemCount &amp;lt;= items.size(); itemCount++) {&lt;br /&gt;
         int itemWeight = items[itemCount - 1];&lt;br /&gt;
         for (int sum = 0; sum &amp;lt;= targetSum; sum++)&lt;br /&gt;
             if (canMake[itemCount - 1][sum] ||&lt;br /&gt;
                 sum &amp;gt;= itemWeight &amp;amp;&amp;amp; canMake[itemCount - 1][sum - itemWeight])&lt;br /&gt;
                 canMake[itemCount][sum] = true;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return canMake[items.size()][targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
 bool canMakeSum(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; canMake(items.size() + 1, vector&amp;lt;bool&amp;gt;(targetSum + 1));&lt;br /&gt;
     canMake[0][0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemCount = 1; itemCount &amp;lt;= items.size(); itemCount++) {&lt;br /&gt;
         int itemWeight = items[itemCount - 1];&lt;br /&gt;
         for (int sum = 0; sum &amp;lt;= targetSum; sum++)&lt;br /&gt;
             if (canMake[itemCount - 1][sum] ||&lt;br /&gt;
                 sum &amp;gt;= itemWeight &amp;amp;&amp;amp; canMake[itemCount][sum - itemWeight])&lt;br /&gt;
                 canMake[itemCount][sum] = true;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return canMake[items.size()][targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== O(items.size() * targetSum) памяти, вернуть сертификат (список предметов) ====&lt;br /&gt;
{|&lt;br /&gt;
|width=50%| Предмет каждого вида ровно один&lt;br /&gt;
| &amp;amp;nbsp;&lt;br /&gt;
|width=50%| Предметов каждого вида неограниченно много&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
 vector&amp;lt;int&amp;gt; canMakeSum(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; canMake(items.size() + 1, vector&amp;lt;bool&amp;gt;(targetSum + 1));&lt;br /&gt;
     canMake[0][0] = true;&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;&amp;gt; from(items.size() + 1, vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;(targetSum + 1));&lt;br /&gt;
 &lt;br /&gt;
     for (int itemCount = 1; itemCount &amp;lt;= items.size(); itemCount++) {&lt;br /&gt;
         int itemWeight = items[itemCount - 1];&lt;br /&gt;
         for (int sum = 0; sum &amp;lt;= targetSum; sum++) {&lt;br /&gt;
             if (canMake[itemCount - 1][sum]) {&lt;br /&gt;
                 canMake[itemCount][sum] = true;&lt;br /&gt;
                 from[itemCount][sum] = { itemCount - 1, sum };&lt;br /&gt;
             } else if (sum &amp;gt;= itemWeight &amp;amp;&amp;amp; canMake[itemCount - 1][sum - itemWeight]) {&lt;br /&gt;
                 canMake[itemCount][sum] = true;&lt;br /&gt;
                 from[itemCount][sum] = { itemCount - 1, sum - itemWeight };&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     if (!canMake[items.size()][targetSum])&lt;br /&gt;
         return {};&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;int&amp;gt; takenItems;&lt;br /&gt;
     for (int itemCount = items.size(), sum = targetSum; itemCount &amp;gt; 0; ) {&lt;br /&gt;
         auto &amp;amp;[prevItemCount, prevSum] = from[itemCount][sum];&lt;br /&gt;
         if (prevSum != sum)&lt;br /&gt;
             takenItems.push_back(sum - prevSum);&lt;br /&gt;
         itemCount = prevItemCount;&lt;br /&gt;
         sum = prevSum;&lt;br /&gt;
     }&lt;br /&gt;
     return takenItems;&lt;br /&gt;
 }&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
 vector&amp;lt;int&amp;gt; canMakeSum(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; canMake(items.size() + 1, vector&amp;lt;bool&amp;gt;(targetSum + 1));&lt;br /&gt;
     canMake[0][0] = true;&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;&amp;gt; from(items.size() + 1, vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;(targetSum + 1));&lt;br /&gt;
 &lt;br /&gt;
     for (int itemCount = 1; itemCount &amp;lt;= items.size(); itemCount++) {&lt;br /&gt;
         int itemWeight = items[itemCount - 1];&lt;br /&gt;
         for (int sum = 0; sum &amp;lt;= targetSum; sum++) {&lt;br /&gt;
             if (canMake[itemCount - 1][sum]) {&lt;br /&gt;
                 canMake[itemCount][sum] = true;&lt;br /&gt;
                 from[itemCount][sum] = { itemCount - 1, sum };&lt;br /&gt;
             } else if (sum &amp;gt;= itemWeight &amp;amp;&amp;amp; canMake[itemCount][sum - itemWeight]) {&lt;br /&gt;
                 canMake[itemCount][sum] = true;&lt;br /&gt;
                 from[itemCount][sum] = { itemCount, sum - itemWeight };&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     if (!canMake[items.size()][targetSum])&lt;br /&gt;
         return {};&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;int&amp;gt; takenItems;&lt;br /&gt;
     for (int itemCount = items.size(), sum = targetSum; itemCount &amp;gt; 0; ) {&lt;br /&gt;
         auto &amp;amp;[prevItemCount, prevSum] = from[itemCount][sum];&lt;br /&gt;
         if (prevSum != sum)&lt;br /&gt;
             takenItems.push_back(sum - prevSum);&lt;br /&gt;
         itemCount = prevItemCount;&lt;br /&gt;
         sum = prevSum;&lt;br /&gt;
     }&lt;br /&gt;
     return takenItems;&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== O(targetSum) памяти ====&lt;br /&gt;
{|&lt;br /&gt;
|width=50%| Предмет каждого вида ровно один&lt;br /&gt;
| &amp;amp;nbsp;&lt;br /&gt;
|width=50%| Предметов каждого вида неограниченно много&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
 bool canMakeSum(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;bool&amp;gt; canMake(targetSum + 1);&lt;br /&gt;
     canMake[0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemWeight : items)&lt;br /&gt;
         for (int sum = targetSum; sum &amp;gt;= itemWeight; sum--)&lt;br /&gt;
             if (canMake[sum - itemWeight])&lt;br /&gt;
                 canMake[sum] = true;&lt;br /&gt;
 &lt;br /&gt;
     return canMake[targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
 bool canMakeSum(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;bool&amp;gt; canMake(targetSum + 1);&lt;br /&gt;
     canMake[0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemWeight : items)&lt;br /&gt;
         for (int sum = itemWeight; sum &amp;lt;= targetSum; sum++)&lt;br /&gt;
             if (canMake[sum - itemWeight])&lt;br /&gt;
                 canMake[sum] = true;&lt;br /&gt;
 &lt;br /&gt;
     return canMake[targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== O(targetSum) памяти, вернуть сертификат (список предметов) ====&lt;br /&gt;
{|&lt;br /&gt;
|width=50%| Предмет каждого вида ровно один&lt;br /&gt;
| &amp;amp;nbsp;&lt;br /&gt;
|width=50%| Предметов каждого вида неограниченно много&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
 vector&amp;lt;int&amp;gt; canMakeSum(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;bool&amp;gt; canMake(targetSum + 1);&lt;br /&gt;
     canMake[0] = true;&lt;br /&gt;
     vector&amp;lt;int&amp;gt; from(targetSum + 1);&lt;br /&gt;
 &lt;br /&gt;
     for (int itemWeight : items) {&lt;br /&gt;
         for (int sum = targetSum; sum &amp;gt;= itemWeight; sum--) {&lt;br /&gt;
             if (!canMake[sum] &amp;amp;&amp;amp; canMake[sum - itemWeight]) {&lt;br /&gt;
                 canMake[sum] = true;&lt;br /&gt;
                 from[sum] = sum - itemWeight;&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     if (!canMake[targetSum])&lt;br /&gt;
         return {};&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;int&amp;gt; takenItems;&lt;br /&gt;
     for (int sum = targetSum; sum &amp;gt; 0; sum = from[sum])&lt;br /&gt;
         takenItems.push_back(sum - from[sum]);&lt;br /&gt;
     return takenItems;&lt;br /&gt;
 }&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
 vector&amp;lt;int&amp;gt; canMakeSum(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;bool&amp;gt; canMake(targetSum + 1);&lt;br /&gt;
     canMake[0] = true;&lt;br /&gt;
     vector&amp;lt;int&amp;gt; from(targetSum + 1);&lt;br /&gt;
 &lt;br /&gt;
     for (int itemWeight : items) {&lt;br /&gt;
         for (int sum = itemWeight; sum &amp;lt;= targetSum; sum++) {&lt;br /&gt;
             if (!canMake[sum] &amp;amp;&amp;amp; canMake[sum - itemWeight]) {&lt;br /&gt;
                 canMake[sum] = true;&lt;br /&gt;
                 from[sum] = sum - itemWeight;&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     if (!canMake[targetSum])&lt;br /&gt;
         return {};&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;int&amp;gt; takenItems;&lt;br /&gt;
     for (int sum = targetSum; sum &amp;gt; 0; sum = from[sum])&lt;br /&gt;
         takenItems.push_back(sum - from[sum]);&lt;br /&gt;
     return takenItems;&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Каким минимальным количеством предметом можно набрать сумму targetSum ===&lt;br /&gt;
&lt;br /&gt;
==== O(items.size() * targetSum) памяти ====&lt;br /&gt;
{|&lt;br /&gt;
|width=50%| Предмет каждого вида ровно один&lt;br /&gt;
| &amp;amp;nbsp;&lt;br /&gt;
|width=50%| Предметов каждого вида неограниченно много&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
 int getMinCount(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; minCount(items.size() + 1, vector&amp;lt;int&amp;gt;(targetSum + 1, 1e9));&lt;br /&gt;
     minCount[0][0] = 0;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemCount = 1; itemCount &amp;lt;= items.size(); itemCount++) {&lt;br /&gt;
         int itemWeight = items[itemCount - 1];&lt;br /&gt;
         for (int sum = 0; sum &amp;lt;= targetSum; sum++) {&lt;br /&gt;
             if (minCount[itemCount][sum] &amp;gt; minCount[itemCount - 1][sum])&lt;br /&gt;
                 minCount[itemCount][sum] = minCount[itemCount - 1][sum];&lt;br /&gt;
             if (sum &amp;gt;= itemWeight &amp;amp;&amp;amp; minCount[itemCount][sum] &amp;gt; minCount[itemCount - 1][sum - itemWeight] + 1)&lt;br /&gt;
                 minCount[itemCount][sum] = minCount[itemCount - 1][sum - itemWeight] + 1;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return minCount[items.size()][targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
 int getMinCount(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; minCount(items.size() + 1, vector&amp;lt;int&amp;gt;(targetSum + 1, 1e9));&lt;br /&gt;
     minCount[0][0] = 0;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemCount = 1; itemCount &amp;lt;= items.size(); itemCount++) {&lt;br /&gt;
         int itemWeight = items[itemCount - 1];&lt;br /&gt;
         for (int sum = 0; sum &amp;lt;= targetSum; sum++) {&lt;br /&gt;
             if (minCount[itemCount][sum] &amp;gt; minCount[itemCount - 1][sum])&lt;br /&gt;
                 minCount[itemCount][sum] = minCount[itemCount - 1][sum];&lt;br /&gt;
             if (sum &amp;gt;= itemWeight &amp;amp;&amp;amp; minCount[itemCount][sum] &amp;gt; minCount[itemCount][sum - itemWeight] + 1)&lt;br /&gt;
                 minCount[itemCount][sum] = minCount[itemCount][sum - itemWeight] + 1;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return minCount[items.size()][targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== O(items.size() * targetSum) памяти, вернуть сертификат (список предметов) ====&lt;br /&gt;
{|&lt;br /&gt;
|width=50%| Предмет каждого вида ровно один&lt;br /&gt;
| &amp;amp;nbsp;&lt;br /&gt;
|width=50%| Предметов каждого вида неограниченно много&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
 vector&amp;lt;int&amp;gt; getMinCount(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; minCount(items.size() + 1, vector&amp;lt;int&amp;gt;(targetSum + 1, 1e9));&lt;br /&gt;
     minCount[0][0] = 0;&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;&amp;gt; from(items.size() + 1, vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;(targetSum + 1));&lt;br /&gt;
 &lt;br /&gt;
     for (int itemCount = 1; itemCount &amp;lt;= items.size(); itemCount++) {&lt;br /&gt;
         int itemWeight = items[itemCount - 1];&lt;br /&gt;
         for (int sum = 0; sum &amp;lt;= targetSum; sum++) {&lt;br /&gt;
             if (minCount[itemCount][sum] &amp;gt; minCount[itemCount - 1][sum]) {&lt;br /&gt;
                 minCount[itemCount][sum] = minCount[itemCount - 1][sum];&lt;br /&gt;
                 from[itemCount][sum] = { itemCount - 1, sum };&lt;br /&gt;
             }&lt;br /&gt;
             if (sum &amp;gt;= itemWeight &amp;amp;&amp;amp; minCount[itemCount][sum] &amp;gt; minCount[itemCount - 1][sum - itemWeight] + 1) {&lt;br /&gt;
                 minCount[itemCount][sum] = minCount[itemCount - 1][sum - itemWeight] + 1;&lt;br /&gt;
                 from[itemCount][sum] = { itemCount - 1, sum - itemWeight };&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     if (minCount[items.size()][targetSum] == 1e9)&lt;br /&gt;
         return {};&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;int&amp;gt; takenItems;&lt;br /&gt;
     for (int itemCount = items.size(), sum = targetSum; itemCount &amp;gt; 0; ) {&lt;br /&gt;
         auto &amp;amp;[prevItemCount, prevSum] = from[itemCount][sum];&lt;br /&gt;
         if (prevSum != sum)&lt;br /&gt;
             takenItems.push_back(sum - prevSum);&lt;br /&gt;
         itemCount = prevItemCount;&lt;br /&gt;
         sum = prevSum;&lt;br /&gt;
     }&lt;br /&gt;
     return takenItems;&lt;br /&gt;
 }&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
 vector&amp;lt;int&amp;gt; getMinCount(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; minCount(items.size() + 1, vector&amp;lt;int&amp;gt;(targetSum + 1, 1e9));&lt;br /&gt;
     minCount[0][0] = 0;&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;&amp;gt; from(items.size() + 1, vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;(targetSum + 1));&lt;br /&gt;
 &lt;br /&gt;
     for (int itemCount = 1; itemCount &amp;lt;= items.size(); itemCount++) {&lt;br /&gt;
         int itemWeight = items[itemCount - 1];&lt;br /&gt;
         for (int sum = 0; sum &amp;lt;= targetSum; sum++) {&lt;br /&gt;
             if (minCount[itemCount][sum] &amp;gt; minCount[itemCount - 1][sum]) {&lt;br /&gt;
                 minCount[itemCount][sum] = minCount[itemCount - 1][sum];&lt;br /&gt;
                 from[itemCount][sum] = { itemCount - 1, sum };&lt;br /&gt;
             }&lt;br /&gt;
             if (sum &amp;gt;= itemWeight &amp;amp;&amp;amp; minCount[itemCount][sum] &amp;gt; minCount[itemCount][sum - itemWeight] + 1) {&lt;br /&gt;
                 minCount[itemCount][sum] = minCount[itemCount][sum - itemWeight] + 1;&lt;br /&gt;
                 from[itemCount][sum] = { itemCount, sum - itemWeight };&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     if (minCount[items.size()][targetSum] == 1e9)&lt;br /&gt;
         return {};&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;int&amp;gt; takenItems;&lt;br /&gt;
     for (int itemCount = items.size(), sum = targetSum; itemCount &amp;gt; 0; ) {&lt;br /&gt;
         auto &amp;amp;[prevItemCount, prevSum] = from[itemCount][sum];&lt;br /&gt;
         if (prevSum != sum)&lt;br /&gt;
             takenItems.push_back(sum - prevSum);&lt;br /&gt;
         itemCount = prevItemCount;&lt;br /&gt;
         sum = prevSum;&lt;br /&gt;
     }&lt;br /&gt;
     return takenItems;&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== O(targetSum) памяти ====&lt;br /&gt;
{|&lt;br /&gt;
|width=50%| Предмет каждого вида ровно один&lt;br /&gt;
| &amp;amp;nbsp;&lt;br /&gt;
|width=50%| Предметов каждого вида неограниченно много&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
 int getMinCount(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;int&amp;gt; minCount(targetSum + 1, 1e9);&lt;br /&gt;
     minCount[0] = 0;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemWeight : items)&lt;br /&gt;
         for (int sum = targetSum; sum &amp;gt;= itemWeight; sum--)&lt;br /&gt;
             if (minCount[sum] &amp;gt; minCount[sum - itemWeight] + 1)&lt;br /&gt;
                 minCount[sum] = minCount[sum - itemWeight] + 1;&lt;br /&gt;
 &lt;br /&gt;
     return minCount[targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
 int getMinCount(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;int&amp;gt; minCount(targetSum + 1, 1e9);&lt;br /&gt;
     minCount[0] = 0;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemWeight : items)&lt;br /&gt;
         for (int sum = itemWeight; sum &amp;lt;= targetSum; sum++)&lt;br /&gt;
             if (minCount[sum] &amp;gt; minCount[sum - itemWeight] + 1)&lt;br /&gt;
                 minCount[sum] = minCount[sum - itemWeight] + 1;&lt;br /&gt;
 &lt;br /&gt;
     return minCount[targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== O(targetSum) памяти, вернуть сертификат (список предметов) ====&lt;br /&gt;
{|&lt;br /&gt;
|width=50%| Предмет каждого вида ровно один&lt;br /&gt;
| &amp;amp;nbsp;&lt;br /&gt;
|width=50%| Предметов каждого вида неограниченно много&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
(Невозможно. Контртест: items = { 1, 2, 3 }, targetSum = 6. Ожидается ответ { 1, 2, 3 }, будет получен ответ { 3, 3 }.)&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
 vector&amp;lt;int&amp;gt; getMinCount(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;int&amp;gt; minCount(targetSum + 1, 1e9);&lt;br /&gt;
     minCount[0] = 0;&lt;br /&gt;
     vector&amp;lt;int&amp;gt; from(targetSum + 1);&lt;br /&gt;
 &lt;br /&gt;
     for (int itemWeight : items) {&lt;br /&gt;
         for (int sum = itemWeight; sum &amp;lt;= targetSum; sum++) {&lt;br /&gt;
             if (minCount[sum] &amp;gt; minCount[sum - itemWeight] + 1) {&lt;br /&gt;
                 minCount[sum] = minCount[sum - itemWeight] + 1;&lt;br /&gt;
                 from[sum] = sum - itemWeight;&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     if (minCount[targetSum] == 1e9)&lt;br /&gt;
         return {};&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;int&amp;gt; takenItems;&lt;br /&gt;
     for (int sum = targetSum; sum &amp;gt; 0; sum = from[sum])&lt;br /&gt;
         takenItems.push_back(sum - from[sum]);&lt;br /&gt;
     return takenItems;&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Сколько есть способов набрать сумму targetSum (без учёта порядка, 1 + 2 == 2 + 1) ===&lt;br /&gt;
&lt;br /&gt;
==== O(items.size() * targetSum) памяти ====&lt;br /&gt;
{|&lt;br /&gt;
|width=50%| Предмет каждого вида ровно один&lt;br /&gt;
| &amp;amp;nbsp;&lt;br /&gt;
|width=50%| Предметов каждого вида неограниченно много&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
 long long getWays(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;long long&amp;gt;&amp;gt; ways(items.size() + 1, vector&amp;lt;long long&amp;gt;(targetSum + 1));&lt;br /&gt;
     ways[0][0] = 1;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemCount = 1; itemCount &amp;lt;= items.size(); itemCount++) {&lt;br /&gt;
         int itemWeight = items[itemCount - 1];&lt;br /&gt;
         for (int sum = 0; sum &amp;lt;= targetSum; sum++) {&lt;br /&gt;
             ways[itemCount][sum] += ways[itemCount - 1][sum];&lt;br /&gt;
             if (sum &amp;gt;= itemWeight)&lt;br /&gt;
                 ways[itemCount][sum] += ways[itemCount - 1][sum - itemWeight];&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return ways[items.size()][targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
 long long getWays(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;long long&amp;gt;&amp;gt; ways(items.size() + 1, vector&amp;lt;long long&amp;gt;(targetSum + 1));&lt;br /&gt;
     ways[0][0] = 1;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemCount = 1; itemCount &amp;lt;= items.size(); itemCount++) {&lt;br /&gt;
         int itemWeight = items[itemCount - 1];&lt;br /&gt;
         for (int sum = 0; sum &amp;lt;= targetSum; sum++) {&lt;br /&gt;
             ways[itemCount][sum] += ways[itemCount - 1][sum];&lt;br /&gt;
             if (sum &amp;gt;= itemWeight)&lt;br /&gt;
                 ways[itemCount][sum] += ways[itemCount][sum - itemWeight];&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return ways[items.size()][targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== O(targetSum) памяти ====&lt;br /&gt;
{|&lt;br /&gt;
|width=50%| Предмет каждого вида ровно один&lt;br /&gt;
| &amp;amp;nbsp;&lt;br /&gt;
|width=50%| Предметов каждого вида неограниченно много&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
 long long getWays(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;long long&amp;gt; ways(targetSum + 1);&lt;br /&gt;
     ways[0] = 1;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemWeight : items)&lt;br /&gt;
         for (int sum = targetSum; sum &amp;gt;= itemWeight; sum--)&lt;br /&gt;
             ways[sum] += ways[sum - itemWeight];&lt;br /&gt;
 &lt;br /&gt;
     return ways[targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
 long long getWays(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;long long&amp;gt; ways(targetSum + 1);&lt;br /&gt;
     ways[0] = 1;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemWeight : items)&lt;br /&gt;
         for (int sum = itemWeight; sum &amp;lt;= targetSum; sum++)&lt;br /&gt;
             ways[sum] += ways[sum - itemWeight];&lt;br /&gt;
 &lt;br /&gt;
     return ways[targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Сколько есть способов набрать сумму targetSum (с учётом порядка, 1 + 2 != 2 + 1) ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|width=50%| Предмет каждого вида ровно один&lt;br /&gt;
| &amp;amp;nbsp;&lt;br /&gt;
|width=50%| Предметов каждого вида неограниченно много&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
(O(targetSum * items.size()) памяти.)&lt;br /&gt;
 long long getOrderedWays(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;long long&amp;gt;&amp;gt; ways(targetSum + 1, vector&amp;lt;long long&amp;gt;(items.size() + 1));&lt;br /&gt;
     ways[0][0] = 1;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemWeight : items)&lt;br /&gt;
         for (int sum = targetSum; sum &amp;gt;= itemWeight; sum--)&lt;br /&gt;
             for (int takenItemCount = 1; takenItemCount &amp;lt;= items.size(); takenItemCount++)&lt;br /&gt;
                 ways[sum][takenItemCount] += ways[sum - itemWeight][takenItemCount - 1] * takenItemCount;&lt;br /&gt;
 &lt;br /&gt;
     long long res = 0;&lt;br /&gt;
     for (int takenItemCount = 0; takenItemCount &amp;lt;= items.size(); takenItemCount++)&lt;br /&gt;
         res += ways[targetSum][takenItemCount];&lt;br /&gt;
     return res;&lt;br /&gt;
 }&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
(O(targetSum) памяти. По сути, это другая формулировка задачи о кузнечике.)&lt;br /&gt;
 long long getOrderedWays(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;long long&amp;gt; ways(targetSum + 1);&lt;br /&gt;
     ways[0] = 1;&lt;br /&gt;
 &lt;br /&gt;
     for (int sum = 1; sum &amp;lt;= targetSum; sum++)&lt;br /&gt;
         for (int itemWeight : items)&lt;br /&gt;
             if (sum &amp;gt;= itemWeight)&lt;br /&gt;
                 ways[sum] += ways[sum - itemWeight];&lt;br /&gt;
 &lt;br /&gt;
     return ways[targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
* [http://algorithmica.org/tg/knapsack-gis-gcs algorithmica.org — Задача о рюкзаке, НВП и НОП]&lt;br /&gt;
* [https://brilliant.org/wiki/backpack-problem/ brilliant.org — Backpack Problem]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Динамическое программирование]]&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=%D0%97%D0%B0%D0%B4%D0%B0%D1%87%D0%B0_%D0%BE_%D1%80%D1%8E%D0%BA%D0%B7%D0%B0%D0%BA%D0%B5_%D0%B8_%D1%81%D0%B2%D1%8F%D0%B7%D0%B0%D0%BD%D0%BD%D1%8B%D0%B5_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8&amp;diff=2936</id>
		<title>Задача о рюкзаке и связанные задачи</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=%D0%97%D0%B0%D0%B4%D0%B0%D1%87%D0%B0_%D0%BE_%D1%80%D1%8E%D0%BA%D0%B7%D0%B0%D0%BA%D0%B5_%D0%B8_%D1%81%D0%B2%D1%8F%D0%B7%D0%B0%D0%BD%D0%BD%D1%8B%D0%B5_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8&amp;diff=2936"/>
		<updated>2025-11-19T00:03:05Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: /* Примеры кода */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__TOC__ &lt;br /&gt;
&lt;br /&gt;
== Задача о сумме подмножеств (subset sum problem) ==&lt;br /&gt;
&lt;br /&gt;
=== Можно ли набрать заданный вес ===&lt;br /&gt;
Есть несколько предметов, для каждого известен вес. Можем ли мы выбрать несколько предметов так, чтобы общий вес был в точности равен заданному числу?&lt;br /&gt;
&lt;br /&gt;
 vector&amp;lt;int&amp;gt; itemWeight; // веса предметов&lt;br /&gt;
 int targetWeight;       // вес, который нужно набрать&lt;br /&gt;
&lt;br /&gt;
Будем заполнять таблицу canMake[][]. Строки таблицы соответствуют количеству рассмотренных предметов: нулевая строка — ноль предметов, первая строка — один предмет, ..., N-я строка — N предметов. Столбцы таблицы соответствуют весам от 0 до заданного веса. В ячейках хранятся true и false: canMake[items][weight] == true, если мы можем набрать вес weight, используя некоторые из items первых предметов.&lt;br /&gt;
&lt;br /&gt;
 vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; canMake(itemWeight.size() + 1, vector&amp;lt;int&amp;gt;(targetWeight + 1));&lt;br /&gt;
&lt;br /&gt;
Как должна выглядеть нулевая строка таблицы? В нулевой строке мы описываем ситуацию, когда не рассмотрено ещё ни одного предмета; какие веса мы при этом можем набрать? Очевидно, только вес 0. Поэтому ячейка canMake[0][0] должна содержать true, а остальные ячейки первой строки — false.&lt;br /&gt;
&lt;br /&gt;
       суммарные веса  0  1  2  3  4  5  6  7  8  9 10&lt;br /&gt;
    строка canMake[0]  1  0  0  0  0  0  0  0  0  0  0 &lt;br /&gt;
 (предметов пока нет)&lt;br /&gt;
&lt;br /&gt;
Теперь разберёмся, как должна выглядеть первая строка таблицы (она соответствует первому предмету). Пусть, для определённости, вес первого предмета — 3 (itemWeight[0] == 3). Какие веса мы теперь можем набрать?&lt;br /&gt;
&lt;br /&gt;
Нам всё ещё доступен вес 0 (если мы не будем брать первый предмет), но теперь мы можем получить ещё и вес 3 (если мы возьмём первый предмет).&lt;br /&gt;
&lt;br /&gt;
       суммарные веса  0  1  2  3  4  5  6  7  8  9 10&lt;br /&gt;
    строка canMake[0]  1  0  0  0  0  0  0  0  0  0  0&lt;br /&gt;
 (предметов пока нет)&lt;br /&gt;
    строка canMake[1]  1  0  0  1  0  0  0  0  0  0  0&lt;br /&gt;
   (вес предмета — 3) &lt;br /&gt;
&lt;br /&gt;
Далее — вторая строка таблицы (она соответствует первым двум предметам). Пусть вес второго предмета — 5 (itemWeight[1] == 5). Какие веса мы теперь можем набрать?&lt;br /&gt;
&lt;br /&gt;
Нам всё ещё доступны веса 0 и 3 (если мы не будем брать второй предмет), но теперь мы можем получить ещё веса 5 (если мы до этого не брали ничего, а теперь возьмём второй предмет) и 8 (если мы до этого взяли первый предмет и получили вес 3, а сейчас добавим второй предмет).&lt;br /&gt;
&lt;br /&gt;
       суммарные веса  0  1  2  3  4  5  6  7  8  9 10&lt;br /&gt;
    строка canMake[0]  1  0  0  0  0  0  0  0  0  0  0&lt;br /&gt;
 (предметов пока нет)&lt;br /&gt;
    строка canMake[1]  1  0  0  1  0  0  0  0  0  0  0&lt;br /&gt;
   (вес предмета — 3) &lt;br /&gt;
    строка canMake[2]  1  0  0  1  0  1  0  0  1  0  0&lt;br /&gt;
   (вес предмета — 5)&lt;br /&gt;
&lt;br /&gt;
Далее — третья строка таблицы (она соответствует первым трём предметам). Пусть вес третьего предмета — 2 (itemWeight[2] == 2).&lt;br /&gt;
&lt;br /&gt;
Раньше (без третьего предмета) мы могли набрать веса 0, 3, 5 и 8. Теперь (с третьим предметом) можем дополнительно набрать 2, 5, 7 и 10. Обратите внимание, что вес 5 мы могли набрать как без третьего предмета (просто взяв второй предмет с весом 5), так и с ним (взяв первый и третий предметы с весами 3 и 2); ничего необычного в этой ситуации нет.&lt;br /&gt;
&lt;br /&gt;
       суммарные веса  0  1  2  3  4  5  6  7  8  9 10&lt;br /&gt;
    строка canMake[0]  1  0  0  0  0  0  0  0  0  0  0&lt;br /&gt;
 (предметов пока нет)&lt;br /&gt;
    строка canMake[1]  1  0  0  1  0  0  0  0  0  0  0&lt;br /&gt;
   (вес предмета — 3) &lt;br /&gt;
    строка canMake[2]  1  0  0  1  0  1  0  0  1  0  0&lt;br /&gt;
   (вес предмета — 5)&lt;br /&gt;
    строка canMake[3]  1  0  1  1  0  1  0  1  1  0  1&lt;br /&gt;
   (вес предмета — 2)&lt;br /&gt;
&lt;br /&gt;
Сделаем общие выводы о том, как заполняются строки таблицы. Когда будет true в ячейке canMake[items][weight], то есть когда мы можем набрать вес weight первыми items предметами?&lt;br /&gt;
&lt;br /&gt;
* Во-первых, если мы могли набрать вес weight только предыдущими предметами, не используя новый. То есть, если в предыдущей строке на позиции weight было true (canMake[items - 1][weight] == true).&lt;br /&gt;
* Во-вторых, если мы могли набрать вес (weight - currentItemWeight) предыдущими предметами (currentItemWeight — вес текущего предмета). В этом случае, если мы добавим текущий предмет, то мы получим как раз вес weight. То есть, если в предыдущей строке на позиции (weight - currentItemWeight) было true (canMake[items - 1][weight - currentItemWeight] == true).&lt;br /&gt;
&lt;br /&gt;
Теперь мы готовы написать код заполнения таблицы:&lt;br /&gt;
&lt;br /&gt;
 bool solve(const vector&amp;lt;int&amp;gt; &amp;amp;itemWeight, int targetWeight) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; canMake(itemWeight.size() + 1, vector&amp;lt;int&amp;gt;(targetWeight + 1));&lt;br /&gt;
 &lt;br /&gt;
     canMake[0][0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int items = 1; items &amp;lt;= itemWeight.size(); items++) {&lt;br /&gt;
         int currentItemWeight = itemWeight[items - 1];&lt;br /&gt;
 &lt;br /&gt;
         for (int weight = 0; weight &amp;lt;= targetWeight; weight++) {&lt;br /&gt;
             if (canMake[items - 1][weight])&lt;br /&gt;
                 canMake[items][weight] = true;&lt;br /&gt;
 &lt;br /&gt;
             if (weight &amp;gt;= currentItemWeight &amp;amp;&amp;amp; canMake[items - 1][weight - currentItemWeight])&lt;br /&gt;
                 canMake[items][weight] = true;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return canMake[items.size()][targetWeight];&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Ответ на задачу в итоге будет записан в последнюю ячейку последней строки (canMake[itemWeight.size()][targetWeight]).&lt;br /&gt;
&lt;br /&gt;
Временная сложность решения — Θ(NM) (N — количество предметов, M — целевой вес). Для таблицы требуется Θ(NM) памяти.&lt;br /&gt;
&lt;br /&gt;
=== Можно ли набрать заданный вес, каждый предмет можно брать несколько раз ===&lt;br /&gt;
&lt;br /&gt;
Посмотрим, как изменится заполнение таблицы:&lt;br /&gt;
&lt;br /&gt;
       суммарные веса  0  1  2  3  4  5  6  7  8  9 10&lt;br /&gt;
    строка canMake[0]  1  0  0  0  0  0  0  0  0  0  0&lt;br /&gt;
 (предметов пока нет)&lt;br /&gt;
    строка canMake[1]  1  0  0  1  0  0  1  0  0  1  0&lt;br /&gt;
   (вес предмета — 3) &lt;br /&gt;
    строка canMake[2]  1  0  0  1  0  1  1  0  1  1  1&lt;br /&gt;
   (вес предмета — 5)&lt;br /&gt;
    строка canMake[3]  1  0  1  1  1  1  1  1  1  1  1&lt;br /&gt;
   (вес предмета — 2)&lt;br /&gt;
&lt;br /&gt;
canMake[items][weight] == true, если canMake[items - 1][weight] == true или canMake[items][weight - currentItemWeight] == true.&lt;br /&gt;
&lt;br /&gt;
 bool solve(const vector&amp;lt;int&amp;gt; &amp;amp;itemWeight, int targetWeight) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; canMake(itemWeight.size() + 1, vector&amp;lt;int&amp;gt;(targetWeight + 1));&lt;br /&gt;
 &lt;br /&gt;
     canMake[0][0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int items = 1; items &amp;lt;= itemWeight.size(); items++) {&lt;br /&gt;
         int currentItemWeight = itemWeight[items - 1];&lt;br /&gt;
 &lt;br /&gt;
         for (int weight = 0; weight &amp;lt;= targetWeight; weight++) {&lt;br /&gt;
             if (canMake[items - 1][weight])&lt;br /&gt;
                 canMake[items][weight] = true;&lt;br /&gt;
 &lt;br /&gt;
             if (weight &amp;gt;= currentItemWeight &amp;amp;&amp;amp; canMake[{{Changed|items}}][weight - currentItemWeight])&lt;br /&gt;
                 canMake[items][weight] = true;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return canMake[items.size()][targetWeight];&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Временная сложность решения Θ(NM). Для таблицы требуется Θ(NM) памяти.&lt;br /&gt;
&lt;br /&gt;
Заметим, что в данном случае мы можем не делать двумерную таблицу. Нам будет достаточно одной строки canMake[].&lt;br /&gt;
&lt;br /&gt;
 bool solve(const vector&amp;lt;int&amp;gt; &amp;amp;itemWeight, int targetWeight) {&lt;br /&gt;
     vector&amp;lt;bool&amp;gt; canMake(targetWeight + 1);&lt;br /&gt;
 &lt;br /&gt;
     canMake[0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int items = 1; items &amp;lt;= itemWeight.size(); items++) {&lt;br /&gt;
         int currentItemWeight = itemWeight[items - 1];&lt;br /&gt;
 &lt;br /&gt;
         for (int weight = currentItemWeight; weight &amp;lt;= targetWeight; weight++) {&lt;br /&gt;
             if (canMake[weight - currentItemWeight])&lt;br /&gt;
                 canMake[weight] = true;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return canMake[targetWeight];&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Временная сложность решения Θ(NM). Для таблицы требуется Θ(M) памяти.&lt;br /&gt;
&lt;br /&gt;
=== Можно ли набрать заданный вес, Θ(M) памяти ===&lt;br /&gt;
&lt;br /&gt;
Как и в предыдущем случае, мы можем не делать двумерную таблицу. Нам будет достаточно одной строки canMake[].&lt;br /&gt;
&lt;br /&gt;
Здесь, однако, нужно обратить внимание на то, что если мы будем обновлять строку слева направо, то мы будем учитывать один и тот же предмет несколько раз (как в предыдущем случае). То есть, например, если первый предмет имеет вес  3, то мы запишем true в ячейку 3 (так как в ячейке 0 записано true), затем в ячейку 6 (так как в ячейке 3 записано true), затем в ячейку 9 (так как в ячейке 6 записано true)...&lt;br /&gt;
&lt;br /&gt;
Чтобы избежать такой ситуации, следует обновлять строку не слева направо, а справа налево. Тогда в ячейках слева от текущей никогда не будет изменений, связанных с текущим предметом.&lt;br /&gt;
&lt;br /&gt;
 bool solve(const vector&amp;lt;int&amp;gt; &amp;amp;itemWeight, int targetWeight) {&lt;br /&gt;
     vector&amp;lt;bool&amp;gt; canMake(targetWeight + 1);&lt;br /&gt;
 &lt;br /&gt;
     canMake[0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int items = 1; items &amp;lt;= itemWeight.size(); items++) {&lt;br /&gt;
         int currentItemWeight = itemWeight[items - 1];&lt;br /&gt;
 &lt;br /&gt;
         for ({{Changed|1=int weight = targetWeight; weight &amp;gt;= currentItemWeight; weight--}}) {&lt;br /&gt;
             if (canMake[weight - currentItemWeight])&lt;br /&gt;
                 canMake[weight] = true;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return canMake[targetWeight];&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Временная сложность решения Θ(NM). Для таблицы требуется Θ(M) памяти.&lt;br /&gt;
&lt;br /&gt;
=== Можно ли набрать заданный вес, мало предметов, очень большие веса (⋆) ===&lt;br /&gt;
Мы уже не сможем решить такую задачу при помощи динамического программирования, так как при очень большом targetWeight потребуется очень большая таблица, которая не уместится в память (не говоря уже о времени её заполнения).&lt;br /&gt;
&lt;br /&gt;
Если у нас &amp;lt; 25 предметов, можно просто перебрать все их подмножества (рекурсивно или масками, временная сложность O(2^N)).&lt;br /&gt;
&lt;br /&gt;
Если у нас &amp;lt; 50 предметов, кроме перебора понадобится техника meet in the middle. Разделим предметы на две половины. Для каждой половины сделаем set из всех весов, которые можно составить из предметов в этой половине. Дальше нам нужно найти в двух setах два элемента, в сумме дающие targetWeight.&lt;br /&gt;
&lt;br /&gt;
 unordered_set&amp;lt;long long&amp;gt; getTotalWeights(const vector&amp;lt;long long&amp;gt; &amp;amp;itemWeight) {&lt;br /&gt;
     unordered_set&amp;lt;long long&amp;gt; totalWeights;&lt;br /&gt;
 &lt;br /&gt;
     for (int mask = 0; mask &amp;lt; (1 &amp;lt;&amp;lt; itemWeight.size()); mask++) {&lt;br /&gt;
         long long totalWeight = 0;&lt;br /&gt;
 &lt;br /&gt;
         for (int bit = 0; bit &amp;lt; itemWeight.size(); bit++) {&lt;br /&gt;
             if (mask &amp;amp; (1 &amp;lt;&amp;lt; bit)) &lt;br /&gt;
                 totalWeight += itemWeight[bit];&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         totalWeights.push_back(totalWeight);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return totalMasses;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 bool solve(const vector&amp;lt;long long&amp;gt; &amp;amp;itemWeight, long long targetWeight) {&lt;br /&gt;
     auto mid = itemWeight.begin() + itemWeight.size() / 2;&lt;br /&gt;
     unordered_set&amp;lt;long long&amp;gt; aWeights = getTotalWeights({itemWeight.begin(), mid});&lt;br /&gt;
     unordered_set&amp;lt;long long&amp;gt; bWeights = getTotalWeights({mid, itemWeight.end()});&lt;br /&gt;
 &lt;br /&gt;
     for (long long aWeight : aWeights) {&lt;br /&gt;
         if (bWeights.count(targetWeight - aWeight))&lt;br /&gt;
             return true;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Временная сложность решения Θ(2^(N/2)). Для множеств требуется O(2^(N/2)) памяти.&lt;br /&gt;
&lt;br /&gt;
=== Номера предметов, дающих заданный вес ===&lt;br /&gt;
&lt;br /&gt;
Иногда в задаче требуется не только определить, можем ли мы набрать заданный вес, но и вывести сертификат — номера (индексы) предметов, которые мы должны взять.&lt;br /&gt;
&lt;br /&gt;
Вдобавок к таблице canMake[][] сделаем таблицу take[][], в которой будем отмечать, брали ли мы последний предмет. take[items][weight] == true, если, когда мы рассматривали items предметов, мы смогли набрать вес weight, взяв последний предмет (его индекс — (items - 1)).&lt;br /&gt;
&lt;br /&gt;
Заполнение таблицы take[][] происходит одновременно с заполнением таблицы canMake[][]. Всякий раз, когда мы смогли набрать вес при помощи нового предмета, мы ставим true не только в canMake[][], но и в take[][].&lt;br /&gt;
&lt;br /&gt;
 if (weight &amp;gt;= currentItemWeight &amp;amp;&amp;amp; canMake[items - 1][weight - currentItemWeight]) {&lt;br /&gt;
     canMake[items][weight] = true;&lt;br /&gt;
     take[items][weight] = true;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Когда у нас есть заполненная таблица take[][], мы можем по ней восстановить номера использованных предметов. Обозначим за weight вес предметов, которые нам нужно взять (изначально weight == targetWeight).&lt;br /&gt;
&lt;br /&gt;
Как понять, взяли ли мы самый последний предмет? Если take[items][weight] == true, то мы его брали, иначе — не брали. Если мы взяли последний предмет, то weight нужно уменьшить на itemWeight[items - 1].&lt;br /&gt;
&lt;br /&gt;
Как понять, взяли ли мы предпоследний предмет? Если take[items - 1][weight] == true, то мы его брали, иначе — не брали. Если мы взяли предпоследний предмет, то weight нужно уменьшить на itemWeight[items - 2].&lt;br /&gt;
&lt;br /&gt;
И так далее, от конца к началу мы определяем, брали или нет мы каждый из предметов, попутно уменьшая weight.&lt;br /&gt;
&lt;br /&gt;
 {{Changed|vector&amp;lt;int&amp;gt;}} solve(const vector&amp;lt;int&amp;gt; &amp;amp;itemWeight, int targetWeight) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; canMake(itemWeight.size() + 1, vector&amp;lt;int&amp;gt;(targetWeight + 1));&lt;br /&gt;
     {{Changed|vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; take(itemWeight.size() + 1, vector&amp;lt;int&amp;gt;(targetWeight + 1));}}&lt;br /&gt;
 &lt;br /&gt;
     canMake[0][0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int items = 1; items &amp;lt;= itemWeight.size(); items++) {&lt;br /&gt;
         int currentItemWeight = itemWeight[items - 1];&lt;br /&gt;
 &lt;br /&gt;
         for (int weight = 0; weight &amp;lt;= targetWeight; weight++) {&lt;br /&gt;
             if (canMake[items - 1][weight])&lt;br /&gt;
                 canMake[items][weight] = true;&lt;br /&gt;
 &lt;br /&gt;
             if (weight &amp;gt;= currentItemWeight &amp;amp;&amp;amp; canMake[items - 1][weight - currentItemWeight]) {&lt;br /&gt;
                 canMake[items][weight] = true;&lt;br /&gt;
                 {{Changed|1=take[items][weight] = true;}}&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     {{Changed|1=if (!canMake[itemWeight.size()][targetItem])}}&lt;br /&gt;
         {{Changed|1=return {};}}&lt;br /&gt;
 &lt;br /&gt;
     {{Changed|1=vector&amp;lt;int&amp;gt; takenItemIndexes;}}&lt;br /&gt;
     {{Changed|1=for (int items = itemWeight.size(), weight = targetWeight; items &amp;gt; 0; items--) {}}&lt;br /&gt;
         {{Changed|1=if (take[items][weight]) {}}&lt;br /&gt;
             {{Changed|1=takenItemIndexes.push_back(items - 1);}}&lt;br /&gt;
             {{Changed|1=weight -= itemWeight[items - 1];}}&lt;br /&gt;
         {{Changed|1=} }}&lt;br /&gt;
     {{Changed|1=} }}&lt;br /&gt;
     {{Changed|1=return takenItemIndexes;}}&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Временная сложность решения Θ(NM). Для таблиц требуется Θ(NM) памяти.&lt;br /&gt;
&lt;br /&gt;
=== Номера предметов, дающих заданный вес, Θ(M) памяти (⋆) ===&lt;br /&gt;
&lt;br /&gt;
* [https://stackoverflow.com/questions/51941993/0-1-knapsack-find-solution-set-in-space-optimised-implementation stackoverflow.com/questions/51941993/0-1-knapsack-find-solution-set-in-space-optimised-implementation]&lt;br /&gt;
* [https://www.cs.colostate.edu/~cs575dl/Sp2015/Lectures/Knapsack.pdf www.cs.colostate.edu/~cs575dl/Sp2015/Lectures/Knapsack.pdf]&lt;br /&gt;
&lt;br /&gt;
 vector&amp;lt;bool&amp;gt; getCanMake(const vector&amp;lt;int&amp;gt; &amp;amp;itemWeight, int targetWeight, int l, int r) {&lt;br /&gt;
     vector&amp;lt;bool&amp;gt; canMake(targetWeight + 1);&lt;br /&gt;
 &lt;br /&gt;
     canMake[0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemIndex = l; itemIndex &amp;lt;= r; itemIndex++) {&lt;br /&gt;
         int currentItemWeight = itemWeight[itemIndex];&lt;br /&gt;
 &lt;br /&gt;
         for (int weight = currentItemWeight; weight &amp;lt;= targetWeight; weight++) {&lt;br /&gt;
             if (canMake[weight - currentItemWeight])&lt;br /&gt;
                 canMake[weight] = true;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return canMake;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 vector&amp;lt;int&amp;gt; solve(const vector&amp;lt;int&amp;gt; &amp;amp;itemWeight, int targetWeight, int l, int r) {&lt;br /&gt;
     if (l == r)&lt;br /&gt;
         return targetWeight == itemWeight[l] ? { l } : {};&lt;br /&gt;
 &lt;br /&gt;
     int m = l + (r - l) / 2;&lt;br /&gt;
     vector&amp;lt;bool&amp;gt; aCan = getCanMake(itemWeight, targetWeight, l, m);&lt;br /&gt;
     vector&amp;lt;bool&amp;gt; bCan = getCanMake(itemWeight, targetWeight, m + 1, r);&lt;br /&gt;
 &lt;br /&gt;
     int aWeight = 0, bWeight = targetWeight;&lt;br /&gt;
     while (!aCan[aWeight] || !bCan[bWeight]) {&lt;br /&gt;
         aWeight++;&lt;br /&gt;
         bWeight--;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;int&amp;gt; aItemIndexes = solve(itemWeight, aWeight, l, m);&lt;br /&gt;
     vector&amp;lt;int&amp;gt; bItemIndexes = solve(itemWeight, bWeight, m + 1, r);&lt;br /&gt;
     aItemIndexes.insert(aItemIndexes.end(), bIntemIndexes.begin(), bItemIndexes.end());&lt;br /&gt;
     return aItemIndexes;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Временная сложность решения Θ(NM). Для таблицы требуется Θ(M) памяти.&lt;br /&gt;
&lt;br /&gt;
== Вариации на тему задачи о сумме подмножеств ==&lt;br /&gt;
&lt;br /&gt;
=== Какой максимальный вес не больше заданного можно набрать ===&lt;br /&gt;
&lt;br /&gt;
Решаем задачу о сумме подмножеств, ищем самое правое true в последней строке таблицы. &lt;br /&gt;
&lt;br /&gt;
 {{Changed|int}} solve(const vector&amp;lt;int&amp;gt; &amp;amp;itemWeight, int targetWeight) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; canMake(itemWeight.size() + 1, vector&amp;lt;int&amp;gt;(targetWeight + 1));&lt;br /&gt;
 &lt;br /&gt;
     canMake[0][0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int items = 1; items &amp;lt;= itemWeight.size(); items++) {&lt;br /&gt;
         int currentItemWeight = itemWeight[items - 1];&lt;br /&gt;
 &lt;br /&gt;
         for (int weight = 0; weight &amp;lt;= targetWeight; weight++) {&lt;br /&gt;
             if (canMake[items - 1][weight])&lt;br /&gt;
                 canMake[items][weight] = true;&lt;br /&gt;
 &lt;br /&gt;
             if (weight &amp;gt;= currentItemWeight &amp;amp;&amp;amp; canMake[items - 1][weight - currentItemWeight])&lt;br /&gt;
                 canMake[items][weight] = true;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     {{Changed|1=while (!canMake[items.size()][targetWeight])}}&lt;br /&gt;
        {{Changed|1=targetWeight--;}}&lt;br /&gt;
     {{Changed|1=return targetWeight;}}&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Временная сложность решения Θ(NM). Для таблицы требуется Θ(NM) или Θ(M) памяти.&lt;br /&gt;
&lt;br /&gt;
=== Можно ли разделить предметы на два набора одинакового веса ===&lt;br /&gt;
&lt;br /&gt;
Решаем задачу о сумме подмножеств для targetWeight == (сумма весов предметов) / 2. &lt;br /&gt;
&lt;br /&gt;
 bool solve(const vector&amp;lt;int&amp;gt; &amp;amp;itemWeight) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; canMake(itemWeight.size() + 1, vector&amp;lt;int&amp;gt;(targetWeight + 1));&lt;br /&gt;
 &lt;br /&gt;
     {{Changed|1=int targetWeight = 0;}}&lt;br /&gt;
     {{Changed|1=for (int weight : itemWeight)}}&lt;br /&gt;
         {{Changed|1=targetWeight += weight;}}&lt;br /&gt;
     {{Changed|1=targetWeight /= 2;}}&lt;br /&gt;
 &lt;br /&gt;
     canMake[0][0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int items = 1; items &amp;lt;= itemWeight.size(); items++) {&lt;br /&gt;
         int currentItemWeight = itemWeight[items - 1];&lt;br /&gt;
 &lt;br /&gt;
         for (int weight = 0; weight &amp;lt;= targetWeight; weight++) {&lt;br /&gt;
             if (canMake[items - 1][weight])&lt;br /&gt;
                 canMake[items][weight] = true;&lt;br /&gt;
 &lt;br /&gt;
             if (weight &amp;gt;= currentItemWeight &amp;amp;&amp;amp; canMake[items - 1][weight - currentItemWeight])&lt;br /&gt;
                 canMake[items][weight] = true;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return canMake[items.size()][targetWeight];&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Временная сложность решения Θ(NM). Для таблицы требуется Θ(NM) или Θ(M) памяти.&lt;br /&gt;
&lt;br /&gt;
== Примеры кода ==&lt;br /&gt;
&lt;br /&gt;
=== Можно ли набрать сумму targetSum ===&lt;br /&gt;
&lt;br /&gt;
==== Базовый вариант ====&lt;br /&gt;
{|&lt;br /&gt;
|width=50%| Предмет каждого вида ровно один&lt;br /&gt;
| &amp;amp;nbsp;&lt;br /&gt;
|width=50%| Предметов каждого вида неограниченно много&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
 bool canMakeSum(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; canMake(items.size() + 1, vector&amp;lt;bool&amp;gt;(targetSum + 1));&lt;br /&gt;
     canMake[0][0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemCount = 1; itemCount &amp;lt;= items.size(); itemCount++) {&lt;br /&gt;
         int itemWeight = items[itemCount - 1];&lt;br /&gt;
         for (int sum = 0; sum &amp;lt;= targetSum; sum++)&lt;br /&gt;
             if (canMake[itemCount - 1][sum] ||&lt;br /&gt;
                 sum &amp;gt;= itemWeight &amp;amp;&amp;amp; canMake[itemCount - 1][sum - itemWeight])&lt;br /&gt;
                 canMake[itemCount][sum] = true;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return canMake[items.size()][targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
 bool canMakeSum(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; canMake(items.size() + 1, vector&amp;lt;bool&amp;gt;(targetSum + 1));&lt;br /&gt;
     canMake[0][0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemCount = 1; itemCount &amp;lt;= items.size(); itemCount++) {&lt;br /&gt;
         int itemWeight = items[itemCount - 1];&lt;br /&gt;
         for (int sum = 0; sum &amp;lt;= targetSum; sum++)&lt;br /&gt;
             if (canMake[itemCount - 1][sum] ||&lt;br /&gt;
                 sum &amp;gt;= itemWeight &amp;amp;&amp;amp; canMake[itemCount][sum - itemWeight])&lt;br /&gt;
                 canMake[itemCount][sum] = true;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return canMake[items.size()][targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Вернуть сертификат (список предметов) ====&lt;br /&gt;
{|&lt;br /&gt;
|width=50%| Предмет каждого вида ровно один&lt;br /&gt;
| &amp;amp;nbsp;&lt;br /&gt;
|width=50%| Предметов каждого вида неограниченно много&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
 vector&amp;lt;int&amp;gt; canMakeSum(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; canMake(items.size() + 1, vector&amp;lt;bool&amp;gt;(targetSum + 1));&lt;br /&gt;
     canMake[0][0] = true;&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;&amp;gt; from(items.size() + 1, vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;(targetSum + 1));&lt;br /&gt;
 &lt;br /&gt;
     for (int itemCount = 1; itemCount &amp;lt;= items.size(); itemCount++) {&lt;br /&gt;
         int itemWeight = items[itemCount - 1];&lt;br /&gt;
         for (int sum = 0; sum &amp;lt;= targetSum; sum++) {&lt;br /&gt;
             if (canMake[itemCount - 1][sum]) {&lt;br /&gt;
                 canMake[itemCount][sum] = true;&lt;br /&gt;
                 from[itemCount][sum] = { itemCount - 1, sum };&lt;br /&gt;
             } else if (sum &amp;gt;= itemWeight &amp;amp;&amp;amp; canMake[itemCount - 1][sum - itemWeight]) {&lt;br /&gt;
                 canMake[itemCount][sum] = true;&lt;br /&gt;
                 from[itemCount][sum] = { itemCount - 1, sum - itemWeight };&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     if (!canMake[items.size()][targetSum])&lt;br /&gt;
         return {};&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;int&amp;gt; takenItems;&lt;br /&gt;
     for (int itemCount = items.size(), sum = targetSum; itemCount &amp;gt; 0; ) {&lt;br /&gt;
         auto &amp;amp;[prevItemCount, prevSum] = from[itemCount][sum];&lt;br /&gt;
         if (prevSum != sum)&lt;br /&gt;
             takenItems.push_back(sum - prevSum);&lt;br /&gt;
         itemCount = prevItemCount;&lt;br /&gt;
         sum = prevSum;&lt;br /&gt;
     }&lt;br /&gt;
     return takenItems;&lt;br /&gt;
 }&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
 vector&amp;lt;int&amp;gt; canMakeSum(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; canMake(items.size() + 1, vector&amp;lt;bool&amp;gt;(targetSum + 1));&lt;br /&gt;
     canMake[0][0] = true;&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;&amp;gt; from(items.size() + 1, vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;(targetSum + 1));&lt;br /&gt;
 &lt;br /&gt;
     for (int itemCount = 1; itemCount &amp;lt;= items.size(); itemCount++) {&lt;br /&gt;
         int itemWeight = items[itemCount - 1];&lt;br /&gt;
         for (int sum = 0; sum &amp;lt;= targetSum; sum++) {&lt;br /&gt;
             if (canMake[itemCount - 1][sum]) {&lt;br /&gt;
                 canMake[itemCount][sum] = true;&lt;br /&gt;
                 from[itemCount][sum] = { itemCount - 1, sum };&lt;br /&gt;
             } else if (sum &amp;gt;= itemWeight &amp;amp;&amp;amp; canMake[itemCount][sum - itemWeight]) {&lt;br /&gt;
                 canMake[itemCount][sum] = true;&lt;br /&gt;
                 from[itemCount][sum] = { itemCount, sum - itemWeight };&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     if (!canMake[items.size()][targetSum])&lt;br /&gt;
         return {};&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;int&amp;gt; takenItems;&lt;br /&gt;
     for (int itemCount = items.size(), sum = targetSum; itemCount &amp;gt; 0; ) {&lt;br /&gt;
         auto &amp;amp;[prevItemCount, prevSum] = from[itemCount][sum];&lt;br /&gt;
         if (prevSum != sum)&lt;br /&gt;
             takenItems.push_back(sum - prevSum);&lt;br /&gt;
         itemCount = prevItemCount;&lt;br /&gt;
         sum = prevSum;&lt;br /&gt;
     }&lt;br /&gt;
     return takenItems;&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Базовый вариант, O(targetSum) памяти ====&lt;br /&gt;
{|&lt;br /&gt;
|width=50%| Предмет каждого вида ровно один&lt;br /&gt;
| &amp;amp;nbsp;&lt;br /&gt;
|width=50%| Предметов каждого вида неограниченно много&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
 bool canMakeSum(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;bool&amp;gt; canMake(targetSum + 1);&lt;br /&gt;
     canMake[0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemWeight : items)&lt;br /&gt;
         for (int sum = targetSum; sum &amp;gt;= itemWeight; sum--)&lt;br /&gt;
             if (canMake[sum - itemWeight])&lt;br /&gt;
                 canMake[sum] = true;&lt;br /&gt;
 &lt;br /&gt;
     return canMake[targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
 bool canMakeSum(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;bool&amp;gt; canMake(targetSum + 1);&lt;br /&gt;
     canMake[0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemWeight : items)&lt;br /&gt;
         for (int sum = itemWeight; sum &amp;lt;= targetSum; sum++)&lt;br /&gt;
             if (canMake[sum - itemWeight])&lt;br /&gt;
                 canMake[sum] = true;&lt;br /&gt;
 &lt;br /&gt;
     return canMake[targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Вернуть сертификат (список предметов), O(targetSum) памяти ====&lt;br /&gt;
{|&lt;br /&gt;
|width=50%| Предмет каждого вида ровно один&lt;br /&gt;
| &amp;amp;nbsp;&lt;br /&gt;
|width=50%| Предметов каждого вида неограниченно много&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
 vector&amp;lt;int&amp;gt; canMakeSum(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;bool&amp;gt; canMake(targetSum + 1);&lt;br /&gt;
     canMake[0] = true;&lt;br /&gt;
     vector&amp;lt;int&amp;gt; from(targetSum + 1);&lt;br /&gt;
 &lt;br /&gt;
     for (int itemWeight : items) {&lt;br /&gt;
         for (int sum = targetSum; sum &amp;gt;= itemWeight; sum--) {&lt;br /&gt;
             if (!canMake[sum] &amp;amp;&amp;amp; canMake[sum - itemWeight]) {&lt;br /&gt;
                 canMake[sum] = true;&lt;br /&gt;
                 from[sum] = sum - itemWeight;&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     if (!canMake[targetSum])&lt;br /&gt;
         return {};&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;int&amp;gt; takenItems;&lt;br /&gt;
     for (int sum = targetSum; sum &amp;gt; 0; sum = from[sum])&lt;br /&gt;
         takenItems.push_back(sum - from[sum]);&lt;br /&gt;
     return takenItems;&lt;br /&gt;
 }&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
 vector&amp;lt;int&amp;gt; canMakeSum(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;bool&amp;gt; canMake(targetSum + 1);&lt;br /&gt;
     canMake[0] = true;&lt;br /&gt;
     vector&amp;lt;int&amp;gt; from(targetSum + 1);&lt;br /&gt;
 &lt;br /&gt;
     for (int itemWeight : items) {&lt;br /&gt;
         for (int sum = itemWeight; sum &amp;lt;= targetSum; sum++) {&lt;br /&gt;
             if (!canMake[sum] &amp;amp;&amp;amp; canMake[sum - itemWeight]) {&lt;br /&gt;
                 canMake[sum] = true;&lt;br /&gt;
                 from[sum] = sum - itemWeight;&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     if (!canMake[targetSum])&lt;br /&gt;
         return {};&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;int&amp;gt; takenItems;&lt;br /&gt;
     for (int sum = targetSum; sum &amp;gt; 0; sum = from[sum])&lt;br /&gt;
         takenItems.push_back(sum - from[sum]);&lt;br /&gt;
     return takenItems;&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Каким минимальным количеством предметом можно набрать сумму targetSum ===&lt;br /&gt;
&lt;br /&gt;
==== Базовый вариант ====&lt;br /&gt;
{|&lt;br /&gt;
|width=50%| Предмет каждого вида ровно один&lt;br /&gt;
| &amp;amp;nbsp;&lt;br /&gt;
|width=50%| Предметов каждого вида неограниченно много&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
 int getMinCount(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; minCount(items.size() + 1, vector&amp;lt;int&amp;gt;(targetSum + 1, 1e9));&lt;br /&gt;
     minCount[0][0] = 0;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemCount = 1; itemCount &amp;lt;= items.size(); itemCount++) {&lt;br /&gt;
         int itemWeight = items[itemCount - 1];&lt;br /&gt;
         for (int sum = 0; sum &amp;lt;= targetSum; sum++) {&lt;br /&gt;
             if (minCount[itemCount][sum] &amp;gt; minCount[itemCount - 1][sum])&lt;br /&gt;
                 minCount[itemCount][sum] = minCount[itemCount - 1][sum];&lt;br /&gt;
             if (sum &amp;gt;= itemWeight &amp;amp;&amp;amp; minCount[itemCount][sum] &amp;gt; minCount[itemCount - 1][sum - itemWeight] + 1)&lt;br /&gt;
                 minCount[itemCount][sum] = minCount[itemCount - 1][sum - itemWeight] + 1;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return minCount[items.size()][targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
 int getMinCount(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; minCount(items.size() + 1, vector&amp;lt;int&amp;gt;(targetSum + 1, 1e9));&lt;br /&gt;
     minCount[0][0] = 0;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemCount = 1; itemCount &amp;lt;= items.size(); itemCount++) {&lt;br /&gt;
         int itemWeight = items[itemCount - 1];&lt;br /&gt;
         for (int sum = 0; sum &amp;lt;= targetSum; sum++) {&lt;br /&gt;
             if (minCount[itemCount][sum] &amp;gt; minCount[itemCount - 1][sum])&lt;br /&gt;
                 minCount[itemCount][sum] = minCount[itemCount - 1][sum];&lt;br /&gt;
             if (sum &amp;gt;= itemWeight &amp;amp;&amp;amp; minCount[itemCount][sum] &amp;gt; minCount[itemCount][sum - itemWeight] + 1)&lt;br /&gt;
                 minCount[itemCount][sum] = minCount[itemCount][sum - itemWeight] + 1;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return minCount[items.size()][targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Вернуть сертификат (список предметов) ====&lt;br /&gt;
{|&lt;br /&gt;
|width=50%| Предмет каждого вида ровно один&lt;br /&gt;
| &amp;amp;nbsp;&lt;br /&gt;
|width=50%| Предметов каждого вида неограниченно много&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
 vector&amp;lt;int&amp;gt; getMinCount(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; minCount(items.size() + 1, vector&amp;lt;int&amp;gt;(targetSum + 1, 1e9));&lt;br /&gt;
     minCount[0][0] = 0;&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;&amp;gt; from(items.size() + 1, vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;(targetSum + 1));&lt;br /&gt;
 &lt;br /&gt;
     for (int itemCount = 1; itemCount &amp;lt;= items.size(); itemCount++) {&lt;br /&gt;
         int itemWeight = items[itemCount - 1];&lt;br /&gt;
         for (int sum = 0; sum &amp;lt;= targetSum; sum++) {&lt;br /&gt;
             if (minCount[itemCount][sum] &amp;gt; minCount[itemCount - 1][sum]) {&lt;br /&gt;
                 minCount[itemCount][sum] = minCount[itemCount - 1][sum];&lt;br /&gt;
                 from[itemCount][sum] = { itemCount - 1, sum };&lt;br /&gt;
             }&lt;br /&gt;
             if (sum &amp;gt;= itemWeight &amp;amp;&amp;amp; minCount[itemCount][sum] &amp;gt; minCount[itemCount - 1][sum - itemWeight] + 1) {&lt;br /&gt;
                 minCount[itemCount][sum] = minCount[itemCount - 1][sum - itemWeight] + 1;&lt;br /&gt;
                 from[itemCount][sum] = { itemCount - 1, sum - itemWeight };&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     if (minCount[items.size()][targetSum] == 1e9)&lt;br /&gt;
         return {};&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;int&amp;gt; takenItems;&lt;br /&gt;
     for (int itemCount = items.size(), sum = targetSum; itemCount &amp;gt; 0; ) {&lt;br /&gt;
         auto &amp;amp;[prevItemCount, prevSum] = from[itemCount][sum];&lt;br /&gt;
         if (prevSum != sum)&lt;br /&gt;
             takenItems.push_back(sum - prevSum);&lt;br /&gt;
         itemCount = prevItemCount;&lt;br /&gt;
         sum = prevSum;&lt;br /&gt;
     }&lt;br /&gt;
     return takenItems;&lt;br /&gt;
 }&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
 vector&amp;lt;int&amp;gt; getMinCount(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; minCount(items.size() + 1, vector&amp;lt;int&amp;gt;(targetSum + 1, 1e9));&lt;br /&gt;
     minCount[0][0] = 0;&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;&amp;gt; from(items.size() + 1, vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;(targetSum + 1));&lt;br /&gt;
 &lt;br /&gt;
     for (int itemCount = 1; itemCount &amp;lt;= items.size(); itemCount++) {&lt;br /&gt;
         int itemWeight = items[itemCount - 1];&lt;br /&gt;
         for (int sum = 0; sum &amp;lt;= targetSum; sum++) {&lt;br /&gt;
             if (minCount[itemCount][sum] &amp;gt; minCount[itemCount - 1][sum]) {&lt;br /&gt;
                 minCount[itemCount][sum] = minCount[itemCount - 1][sum];&lt;br /&gt;
                 from[itemCount][sum] = { itemCount - 1, sum };&lt;br /&gt;
             }&lt;br /&gt;
             if (sum &amp;gt;= itemWeight &amp;amp;&amp;amp; minCount[itemCount][sum] &amp;gt; minCount[itemCount][sum - itemWeight] + 1) {&lt;br /&gt;
                 minCount[itemCount][sum] = minCount[itemCount][sum - itemWeight] + 1;&lt;br /&gt;
                 from[itemCount][sum] = { itemCount, sum - itemWeight };&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     if (minCount[items.size()][targetSum] == 1e9)&lt;br /&gt;
         return {};&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;int&amp;gt; takenItems;&lt;br /&gt;
     for (int itemCount = items.size(), sum = targetSum; itemCount &amp;gt; 0; ) {&lt;br /&gt;
         auto &amp;amp;[prevItemCount, prevSum] = from[itemCount][sum];&lt;br /&gt;
         if (prevSum != sum)&lt;br /&gt;
             takenItems.push_back(sum - prevSum);&lt;br /&gt;
         itemCount = prevItemCount;&lt;br /&gt;
         sum = prevSum;&lt;br /&gt;
     }&lt;br /&gt;
     return takenItems;&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Базовый вариант, O(targetSum) памяти ====&lt;br /&gt;
{|&lt;br /&gt;
|width=50%| Предмет каждого вида ровно один&lt;br /&gt;
| &amp;amp;nbsp;&lt;br /&gt;
|width=50%| Предметов каждого вида неограниченно много&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
 int getMinCount(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;int&amp;gt; minCount(targetSum + 1, 1e9);&lt;br /&gt;
     minCount[0] = 0;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemWeight : items)&lt;br /&gt;
         for (int sum = targetSum; sum &amp;gt;= itemWeight; sum--)&lt;br /&gt;
             if (minCount[sum] &amp;gt; minCount[sum - itemWeight] + 1)&lt;br /&gt;
                 minCount[sum] = minCount[sum - itemWeight] + 1;&lt;br /&gt;
 &lt;br /&gt;
     return minCount[targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
 int getMinCount(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;int&amp;gt; minCount(targetSum + 1, 1e9);&lt;br /&gt;
     minCount[0] = 0;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemWeight : items)&lt;br /&gt;
         for (int sum = itemWeight; sum &amp;lt;= targetSum; sum++)&lt;br /&gt;
             if (minCount[sum] &amp;gt; minCount[sum - itemWeight] + 1)&lt;br /&gt;
                 minCount[sum] = minCount[sum - itemWeight] + 1;&lt;br /&gt;
 &lt;br /&gt;
     return minCount[targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Вернуть сертификат (список предметов), O(targetSum) памяти ====&lt;br /&gt;
{|&lt;br /&gt;
|width=50%| Предмет каждого вида ровно один&lt;br /&gt;
| &amp;amp;nbsp;&lt;br /&gt;
|width=50%| Предметов каждого вида неограниченно много&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
(Невозможно. Контртест: items = { 1, 2, 3 }, targetSum = 6. Ожидается ответ { 1, 2, 3 }, будет получен ответ { 3, 3 }.)&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
 vector&amp;lt;int&amp;gt; getMinCount(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;int&amp;gt; minCount(targetSum + 1, 1e9);&lt;br /&gt;
     minCount[0] = 0;&lt;br /&gt;
     vector&amp;lt;int&amp;gt; from(targetSum + 1);&lt;br /&gt;
 &lt;br /&gt;
     for (int itemWeight : items) {&lt;br /&gt;
         for (int sum = itemWeight; sum &amp;lt;= targetSum; sum++) {&lt;br /&gt;
             if (minCount[sum] &amp;gt; minCount[sum - itemWeight] + 1) {&lt;br /&gt;
                 minCount[sum] = minCount[sum - itemWeight] + 1;&lt;br /&gt;
                 from[sum] = sum - itemWeight;&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     if (minCount[targetSum] == 1e9)&lt;br /&gt;
         return {};&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;int&amp;gt; takenItems;&lt;br /&gt;
     for (int sum = targetSum; sum &amp;gt; 0; sum = from[sum])&lt;br /&gt;
         takenItems.push_back(sum - from[sum]);&lt;br /&gt;
     return takenItems;&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Сколько есть способов набрать сумму targetSum (без учёта порядка, 1 + 2 == 2 + 1) ===&lt;br /&gt;
&lt;br /&gt;
==== Базовый вариант ====&lt;br /&gt;
{|&lt;br /&gt;
|width=50%| Предмет каждого вида ровно один&lt;br /&gt;
| &amp;amp;nbsp;&lt;br /&gt;
|width=50%| Предметов каждого вида неограниченно много&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
 long long getWays(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;long long&amp;gt;&amp;gt; ways(items.size() + 1, vector&amp;lt;long long&amp;gt;(targetSum + 1));&lt;br /&gt;
     ways[0][0] = 1;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemCount = 1; itemCount &amp;lt;= items.size(); itemCount++) {&lt;br /&gt;
         int itemWeight = items[itemCount - 1];&lt;br /&gt;
         for (int sum = 0; sum &amp;lt;= targetSum; sum++) {&lt;br /&gt;
             ways[itemCount][sum] += ways[itemCount - 1][sum];&lt;br /&gt;
             if (sum &amp;gt;= itemWeight)&lt;br /&gt;
                 ways[itemCount][sum] += ways[itemCount - 1][sum - itemWeight];&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return ways[items.size()][targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
 long long getWays(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;long long&amp;gt;&amp;gt; ways(items.size() + 1, vector&amp;lt;long long&amp;gt;(targetSum + 1));&lt;br /&gt;
     ways[0][0] = 1;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemCount = 1; itemCount &amp;lt;= items.size(); itemCount++) {&lt;br /&gt;
         int itemWeight = items[itemCount - 1];&lt;br /&gt;
         for (int sum = 0; sum &amp;lt;= targetSum; sum++) {&lt;br /&gt;
             ways[itemCount][sum] += ways[itemCount - 1][sum];&lt;br /&gt;
             if (sum &amp;gt;= itemWeight)&lt;br /&gt;
                 ways[itemCount][sum] += ways[itemCount][sum - itemWeight];&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return ways[items.size()][targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Базовый вариант, O(targetSum) памяти ====&lt;br /&gt;
{|&lt;br /&gt;
|width=50%| Предмет каждого вида ровно один&lt;br /&gt;
| &amp;amp;nbsp;&lt;br /&gt;
|width=50%| Предметов каждого вида неограниченно много&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
 long long getWays(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;long long&amp;gt; ways(targetSum + 1);&lt;br /&gt;
     ways[0] = 1;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemWeight : items)&lt;br /&gt;
         for (int sum = targetSum; sum &amp;gt;= itemWeight; sum--)&lt;br /&gt;
             ways[sum] += ways[sum - itemWeight];&lt;br /&gt;
 &lt;br /&gt;
     return ways[targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
 long long getWays(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;long long&amp;gt; ways(targetSum + 1);&lt;br /&gt;
     ways[0] = 1;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemWeight : items)&lt;br /&gt;
         for (int sum = itemWeight; sum &amp;lt;= targetSum; sum++)&lt;br /&gt;
             ways[sum] += ways[sum - itemWeight];&lt;br /&gt;
 &lt;br /&gt;
     return ways[targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Сколько есть способов набрать сумму targetSum (с учётом порядка, 1 + 2 != 2 + 1) ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|width=50%| Предмет каждого вида ровно один&lt;br /&gt;
| &amp;amp;nbsp;&lt;br /&gt;
|width=50%| Предметов каждого вида неограниченно много&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
 long long getOrderedWays(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;long long&amp;gt;&amp;gt; ways(targetSum + 1, vector&amp;lt;long long&amp;gt;(items.size() + 1));&lt;br /&gt;
     ways[0][0] = 1;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemWeight : items)&lt;br /&gt;
         for (int sum = targetSum; sum &amp;gt;= itemWeight; sum--)&lt;br /&gt;
             for (int takenItemCount = 1; takenItemCount &amp;lt;= items.size(); takenItemCount++)&lt;br /&gt;
                 ways[sum][takenItemCount] += ways[sum - itemWeight][takenItemCount - 1] * takenItemCount;&lt;br /&gt;
 &lt;br /&gt;
     long long res = 0;&lt;br /&gt;
     for (int takenItemCount = 0; takenItemCount &amp;lt;= items.size(); takenItemCount++)&lt;br /&gt;
         res += ways[targetSum][takenItemCount];&lt;br /&gt;
     return res;&lt;br /&gt;
 }&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
 long long getOrderedWays(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;long long&amp;gt; ways(targetSum + 1);&lt;br /&gt;
     ways[0] = 1;&lt;br /&gt;
 &lt;br /&gt;
     for (int sum = 1; sum &amp;lt;= targetSum; sum++)&lt;br /&gt;
         for (int itemWeight : items)&lt;br /&gt;
             if (sum &amp;gt;= itemWeight)&lt;br /&gt;
                 ways[sum] += ways[sum - itemWeight];&lt;br /&gt;
 &lt;br /&gt;
     return ways[targetSum];&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
(O(targetSum) памяти. По сути, это другая формулировка задачи о кузнечике.)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
* [http://algorithmica.org/tg/knapsack-gis-gcs algorithmica.org — Задача о рюкзаке, НВП и НОП]&lt;br /&gt;
* [https://brilliant.org/wiki/backpack-problem/ brilliant.org — Backpack Problem]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Динамическое программирование]]&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=%D0%97%D0%B0%D0%B4%D0%B0%D1%87%D0%B0_%D0%BE_%D1%80%D1%8E%D0%BA%D0%B7%D0%B0%D0%BA%D0%B5_%D0%B8_%D1%81%D0%B2%D1%8F%D0%B7%D0%B0%D0%BD%D0%BD%D1%8B%D0%B5_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8&amp;diff=2935</id>
		<title>Задача о рюкзаке и связанные задачи</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=%D0%97%D0%B0%D0%B4%D0%B0%D1%87%D0%B0_%D0%BE_%D1%80%D1%8E%D0%BA%D0%B7%D0%B0%D0%BA%D0%B5_%D0%B8_%D1%81%D0%B2%D1%8F%D0%B7%D0%B0%D0%BD%D0%BD%D1%8B%D0%B5_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8&amp;diff=2935"/>
		<updated>2025-11-18T23:26:34Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: /* Also */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__TOC__ &lt;br /&gt;
&lt;br /&gt;
== Задача о сумме подмножеств (subset sum problem) ==&lt;br /&gt;
&lt;br /&gt;
=== Можно ли набрать заданный вес ===&lt;br /&gt;
Есть несколько предметов, для каждого известен вес. Можем ли мы выбрать несколько предметов так, чтобы общий вес был в точности равен заданному числу?&lt;br /&gt;
&lt;br /&gt;
 vector&amp;lt;int&amp;gt; itemWeight; // веса предметов&lt;br /&gt;
 int targetWeight;       // вес, который нужно набрать&lt;br /&gt;
&lt;br /&gt;
Будем заполнять таблицу canMake[][]. Строки таблицы соответствуют количеству рассмотренных предметов: нулевая строка — ноль предметов, первая строка — один предмет, ..., N-я строка — N предметов. Столбцы таблицы соответствуют весам от 0 до заданного веса. В ячейках хранятся true и false: canMake[items][weight] == true, если мы можем набрать вес weight, используя некоторые из items первых предметов.&lt;br /&gt;
&lt;br /&gt;
 vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; canMake(itemWeight.size() + 1, vector&amp;lt;int&amp;gt;(targetWeight + 1));&lt;br /&gt;
&lt;br /&gt;
Как должна выглядеть нулевая строка таблицы? В нулевой строке мы описываем ситуацию, когда не рассмотрено ещё ни одного предмета; какие веса мы при этом можем набрать? Очевидно, только вес 0. Поэтому ячейка canMake[0][0] должна содержать true, а остальные ячейки первой строки — false.&lt;br /&gt;
&lt;br /&gt;
       суммарные веса  0  1  2  3  4  5  6  7  8  9 10&lt;br /&gt;
    строка canMake[0]  1  0  0  0  0  0  0  0  0  0  0 &lt;br /&gt;
 (предметов пока нет)&lt;br /&gt;
&lt;br /&gt;
Теперь разберёмся, как должна выглядеть первая строка таблицы (она соответствует первому предмету). Пусть, для определённости, вес первого предмета — 3 (itemWeight[0] == 3). Какие веса мы теперь можем набрать?&lt;br /&gt;
&lt;br /&gt;
Нам всё ещё доступен вес 0 (если мы не будем брать первый предмет), но теперь мы можем получить ещё и вес 3 (если мы возьмём первый предмет).&lt;br /&gt;
&lt;br /&gt;
       суммарные веса  0  1  2  3  4  5  6  7  8  9 10&lt;br /&gt;
    строка canMake[0]  1  0  0  0  0  0  0  0  0  0  0&lt;br /&gt;
 (предметов пока нет)&lt;br /&gt;
    строка canMake[1]  1  0  0  1  0  0  0  0  0  0  0&lt;br /&gt;
   (вес предмета — 3) &lt;br /&gt;
&lt;br /&gt;
Далее — вторая строка таблицы (она соответствует первым двум предметам). Пусть вес второго предмета — 5 (itemWeight[1] == 5). Какие веса мы теперь можем набрать?&lt;br /&gt;
&lt;br /&gt;
Нам всё ещё доступны веса 0 и 3 (если мы не будем брать второй предмет), но теперь мы можем получить ещё веса 5 (если мы до этого не брали ничего, а теперь возьмём второй предмет) и 8 (если мы до этого взяли первый предмет и получили вес 3, а сейчас добавим второй предмет).&lt;br /&gt;
&lt;br /&gt;
       суммарные веса  0  1  2  3  4  5  6  7  8  9 10&lt;br /&gt;
    строка canMake[0]  1  0  0  0  0  0  0  0  0  0  0&lt;br /&gt;
 (предметов пока нет)&lt;br /&gt;
    строка canMake[1]  1  0  0  1  0  0  0  0  0  0  0&lt;br /&gt;
   (вес предмета — 3) &lt;br /&gt;
    строка canMake[2]  1  0  0  1  0  1  0  0  1  0  0&lt;br /&gt;
   (вес предмета — 5)&lt;br /&gt;
&lt;br /&gt;
Далее — третья строка таблицы (она соответствует первым трём предметам). Пусть вес третьего предмета — 2 (itemWeight[2] == 2).&lt;br /&gt;
&lt;br /&gt;
Раньше (без третьего предмета) мы могли набрать веса 0, 3, 5 и 8. Теперь (с третьим предметом) можем дополнительно набрать 2, 5, 7 и 10. Обратите внимание, что вес 5 мы могли набрать как без третьего предмета (просто взяв второй предмет с весом 5), так и с ним (взяв первый и третий предметы с весами 3 и 2); ничего необычного в этой ситуации нет.&lt;br /&gt;
&lt;br /&gt;
       суммарные веса  0  1  2  3  4  5  6  7  8  9 10&lt;br /&gt;
    строка canMake[0]  1  0  0  0  0  0  0  0  0  0  0&lt;br /&gt;
 (предметов пока нет)&lt;br /&gt;
    строка canMake[1]  1  0  0  1  0  0  0  0  0  0  0&lt;br /&gt;
   (вес предмета — 3) &lt;br /&gt;
    строка canMake[2]  1  0  0  1  0  1  0  0  1  0  0&lt;br /&gt;
   (вес предмета — 5)&lt;br /&gt;
    строка canMake[3]  1  0  1  1  0  1  0  1  1  0  1&lt;br /&gt;
   (вес предмета — 2)&lt;br /&gt;
&lt;br /&gt;
Сделаем общие выводы о том, как заполняются строки таблицы. Когда будет true в ячейке canMake[items][weight], то есть когда мы можем набрать вес weight первыми items предметами?&lt;br /&gt;
&lt;br /&gt;
* Во-первых, если мы могли набрать вес weight только предыдущими предметами, не используя новый. То есть, если в предыдущей строке на позиции weight было true (canMake[items - 1][weight] == true).&lt;br /&gt;
* Во-вторых, если мы могли набрать вес (weight - currentItemWeight) предыдущими предметами (currentItemWeight — вес текущего предмета). В этом случае, если мы добавим текущий предмет, то мы получим как раз вес weight. То есть, если в предыдущей строке на позиции (weight - currentItemWeight) было true (canMake[items - 1][weight - currentItemWeight] == true).&lt;br /&gt;
&lt;br /&gt;
Теперь мы готовы написать код заполнения таблицы:&lt;br /&gt;
&lt;br /&gt;
 bool solve(const vector&amp;lt;int&amp;gt; &amp;amp;itemWeight, int targetWeight) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; canMake(itemWeight.size() + 1, vector&amp;lt;int&amp;gt;(targetWeight + 1));&lt;br /&gt;
 &lt;br /&gt;
     canMake[0][0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int items = 1; items &amp;lt;= itemWeight.size(); items++) {&lt;br /&gt;
         int currentItemWeight = itemWeight[items - 1];&lt;br /&gt;
 &lt;br /&gt;
         for (int weight = 0; weight &amp;lt;= targetWeight; weight++) {&lt;br /&gt;
             if (canMake[items - 1][weight])&lt;br /&gt;
                 canMake[items][weight] = true;&lt;br /&gt;
 &lt;br /&gt;
             if (weight &amp;gt;= currentItemWeight &amp;amp;&amp;amp; canMake[items - 1][weight - currentItemWeight])&lt;br /&gt;
                 canMake[items][weight] = true;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return canMake[items.size()][targetWeight];&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Ответ на задачу в итоге будет записан в последнюю ячейку последней строки (canMake[itemWeight.size()][targetWeight]).&lt;br /&gt;
&lt;br /&gt;
Временная сложность решения — Θ(NM) (N — количество предметов, M — целевой вес). Для таблицы требуется Θ(NM) памяти.&lt;br /&gt;
&lt;br /&gt;
=== Можно ли набрать заданный вес, каждый предмет можно брать несколько раз ===&lt;br /&gt;
&lt;br /&gt;
Посмотрим, как изменится заполнение таблицы:&lt;br /&gt;
&lt;br /&gt;
       суммарные веса  0  1  2  3  4  5  6  7  8  9 10&lt;br /&gt;
    строка canMake[0]  1  0  0  0  0  0  0  0  0  0  0&lt;br /&gt;
 (предметов пока нет)&lt;br /&gt;
    строка canMake[1]  1  0  0  1  0  0  1  0  0  1  0&lt;br /&gt;
   (вес предмета — 3) &lt;br /&gt;
    строка canMake[2]  1  0  0  1  0  1  1  0  1  1  1&lt;br /&gt;
   (вес предмета — 5)&lt;br /&gt;
    строка canMake[3]  1  0  1  1  1  1  1  1  1  1  1&lt;br /&gt;
   (вес предмета — 2)&lt;br /&gt;
&lt;br /&gt;
canMake[items][weight] == true, если canMake[items - 1][weight] == true или canMake[items][weight - currentItemWeight] == true.&lt;br /&gt;
&lt;br /&gt;
 bool solve(const vector&amp;lt;int&amp;gt; &amp;amp;itemWeight, int targetWeight) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; canMake(itemWeight.size() + 1, vector&amp;lt;int&amp;gt;(targetWeight + 1));&lt;br /&gt;
 &lt;br /&gt;
     canMake[0][0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int items = 1; items &amp;lt;= itemWeight.size(); items++) {&lt;br /&gt;
         int currentItemWeight = itemWeight[items - 1];&lt;br /&gt;
 &lt;br /&gt;
         for (int weight = 0; weight &amp;lt;= targetWeight; weight++) {&lt;br /&gt;
             if (canMake[items - 1][weight])&lt;br /&gt;
                 canMake[items][weight] = true;&lt;br /&gt;
 &lt;br /&gt;
             if (weight &amp;gt;= currentItemWeight &amp;amp;&amp;amp; canMake[{{Changed|items}}][weight - currentItemWeight])&lt;br /&gt;
                 canMake[items][weight] = true;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return canMake[items.size()][targetWeight];&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Временная сложность решения Θ(NM). Для таблицы требуется Θ(NM) памяти.&lt;br /&gt;
&lt;br /&gt;
Заметим, что в данном случае мы можем не делать двумерную таблицу. Нам будет достаточно одной строки canMake[].&lt;br /&gt;
&lt;br /&gt;
 bool solve(const vector&amp;lt;int&amp;gt; &amp;amp;itemWeight, int targetWeight) {&lt;br /&gt;
     vector&amp;lt;bool&amp;gt; canMake(targetWeight + 1);&lt;br /&gt;
 &lt;br /&gt;
     canMake[0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int items = 1; items &amp;lt;= itemWeight.size(); items++) {&lt;br /&gt;
         int currentItemWeight = itemWeight[items - 1];&lt;br /&gt;
 &lt;br /&gt;
         for (int weight = currentItemWeight; weight &amp;lt;= targetWeight; weight++) {&lt;br /&gt;
             if (canMake[weight - currentItemWeight])&lt;br /&gt;
                 canMake[weight] = true;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return canMake[targetWeight];&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Временная сложность решения Θ(NM). Для таблицы требуется Θ(M) памяти.&lt;br /&gt;
&lt;br /&gt;
=== Можно ли набрать заданный вес, Θ(M) памяти ===&lt;br /&gt;
&lt;br /&gt;
Как и в предыдущем случае, мы можем не делать двумерную таблицу. Нам будет достаточно одной строки canMake[].&lt;br /&gt;
&lt;br /&gt;
Здесь, однако, нужно обратить внимание на то, что если мы будем обновлять строку слева направо, то мы будем учитывать один и тот же предмет несколько раз (как в предыдущем случае). То есть, например, если первый предмет имеет вес  3, то мы запишем true в ячейку 3 (так как в ячейке 0 записано true), затем в ячейку 6 (так как в ячейке 3 записано true), затем в ячейку 9 (так как в ячейке 6 записано true)...&lt;br /&gt;
&lt;br /&gt;
Чтобы избежать такой ситуации, следует обновлять строку не слева направо, а справа налево. Тогда в ячейках слева от текущей никогда не будет изменений, связанных с текущим предметом.&lt;br /&gt;
&lt;br /&gt;
 bool solve(const vector&amp;lt;int&amp;gt; &amp;amp;itemWeight, int targetWeight) {&lt;br /&gt;
     vector&amp;lt;bool&amp;gt; canMake(targetWeight + 1);&lt;br /&gt;
 &lt;br /&gt;
     canMake[0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int items = 1; items &amp;lt;= itemWeight.size(); items++) {&lt;br /&gt;
         int currentItemWeight = itemWeight[items - 1];&lt;br /&gt;
 &lt;br /&gt;
         for ({{Changed|1=int weight = targetWeight; weight &amp;gt;= currentItemWeight; weight--}}) {&lt;br /&gt;
             if (canMake[weight - currentItemWeight])&lt;br /&gt;
                 canMake[weight] = true;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return canMake[targetWeight];&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Временная сложность решения Θ(NM). Для таблицы требуется Θ(M) памяти.&lt;br /&gt;
&lt;br /&gt;
=== Можно ли набрать заданный вес, мало предметов, очень большие веса (⋆) ===&lt;br /&gt;
Мы уже не сможем решить такую задачу при помощи динамического программирования, так как при очень большом targetWeight потребуется очень большая таблица, которая не уместится в память (не говоря уже о времени её заполнения).&lt;br /&gt;
&lt;br /&gt;
Если у нас &amp;lt; 25 предметов, можно просто перебрать все их подмножества (рекурсивно или масками, временная сложность O(2^N)).&lt;br /&gt;
&lt;br /&gt;
Если у нас &amp;lt; 50 предметов, кроме перебора понадобится техника meet in the middle. Разделим предметы на две половины. Для каждой половины сделаем set из всех весов, которые можно составить из предметов в этой половине. Дальше нам нужно найти в двух setах два элемента, в сумме дающие targetWeight.&lt;br /&gt;
&lt;br /&gt;
 unordered_set&amp;lt;long long&amp;gt; getTotalWeights(const vector&amp;lt;long long&amp;gt; &amp;amp;itemWeight) {&lt;br /&gt;
     unordered_set&amp;lt;long long&amp;gt; totalWeights;&lt;br /&gt;
 &lt;br /&gt;
     for (int mask = 0; mask &amp;lt; (1 &amp;lt;&amp;lt; itemWeight.size()); mask++) {&lt;br /&gt;
         long long totalWeight = 0;&lt;br /&gt;
 &lt;br /&gt;
         for (int bit = 0; bit &amp;lt; itemWeight.size(); bit++) {&lt;br /&gt;
             if (mask &amp;amp; (1 &amp;lt;&amp;lt; bit)) &lt;br /&gt;
                 totalWeight += itemWeight[bit];&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         totalWeights.push_back(totalWeight);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return totalMasses;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 bool solve(const vector&amp;lt;long long&amp;gt; &amp;amp;itemWeight, long long targetWeight) {&lt;br /&gt;
     auto mid = itemWeight.begin() + itemWeight.size() / 2;&lt;br /&gt;
     unordered_set&amp;lt;long long&amp;gt; aWeights = getTotalWeights({itemWeight.begin(), mid});&lt;br /&gt;
     unordered_set&amp;lt;long long&amp;gt; bWeights = getTotalWeights({mid, itemWeight.end()});&lt;br /&gt;
 &lt;br /&gt;
     for (long long aWeight : aWeights) {&lt;br /&gt;
         if (bWeights.count(targetWeight - aWeight))&lt;br /&gt;
             return true;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Временная сложность решения Θ(2^(N/2)). Для множеств требуется O(2^(N/2)) памяти.&lt;br /&gt;
&lt;br /&gt;
=== Номера предметов, дающих заданный вес ===&lt;br /&gt;
&lt;br /&gt;
Иногда в задаче требуется не только определить, можем ли мы набрать заданный вес, но и вывести сертификат — номера (индексы) предметов, которые мы должны взять.&lt;br /&gt;
&lt;br /&gt;
Вдобавок к таблице canMake[][] сделаем таблицу take[][], в которой будем отмечать, брали ли мы последний предмет. take[items][weight] == true, если, когда мы рассматривали items предметов, мы смогли набрать вес weight, взяв последний предмет (его индекс — (items - 1)).&lt;br /&gt;
&lt;br /&gt;
Заполнение таблицы take[][] происходит одновременно с заполнением таблицы canMake[][]. Всякий раз, когда мы смогли набрать вес при помощи нового предмета, мы ставим true не только в canMake[][], но и в take[][].&lt;br /&gt;
&lt;br /&gt;
 if (weight &amp;gt;= currentItemWeight &amp;amp;&amp;amp; canMake[items - 1][weight - currentItemWeight]) {&lt;br /&gt;
     canMake[items][weight] = true;&lt;br /&gt;
     take[items][weight] = true;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Когда у нас есть заполненная таблица take[][], мы можем по ней восстановить номера использованных предметов. Обозначим за weight вес предметов, которые нам нужно взять (изначально weight == targetWeight).&lt;br /&gt;
&lt;br /&gt;
Как понять, взяли ли мы самый последний предмет? Если take[items][weight] == true, то мы его брали, иначе — не брали. Если мы взяли последний предмет, то weight нужно уменьшить на itemWeight[items - 1].&lt;br /&gt;
&lt;br /&gt;
Как понять, взяли ли мы предпоследний предмет? Если take[items - 1][weight] == true, то мы его брали, иначе — не брали. Если мы взяли предпоследний предмет, то weight нужно уменьшить на itemWeight[items - 2].&lt;br /&gt;
&lt;br /&gt;
И так далее, от конца к началу мы определяем, брали или нет мы каждый из предметов, попутно уменьшая weight.&lt;br /&gt;
&lt;br /&gt;
 {{Changed|vector&amp;lt;int&amp;gt;}} solve(const vector&amp;lt;int&amp;gt; &amp;amp;itemWeight, int targetWeight) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; canMake(itemWeight.size() + 1, vector&amp;lt;int&amp;gt;(targetWeight + 1));&lt;br /&gt;
     {{Changed|vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; take(itemWeight.size() + 1, vector&amp;lt;int&amp;gt;(targetWeight + 1));}}&lt;br /&gt;
 &lt;br /&gt;
     canMake[0][0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int items = 1; items &amp;lt;= itemWeight.size(); items++) {&lt;br /&gt;
         int currentItemWeight = itemWeight[items - 1];&lt;br /&gt;
 &lt;br /&gt;
         for (int weight = 0; weight &amp;lt;= targetWeight; weight++) {&lt;br /&gt;
             if (canMake[items - 1][weight])&lt;br /&gt;
                 canMake[items][weight] = true;&lt;br /&gt;
 &lt;br /&gt;
             if (weight &amp;gt;= currentItemWeight &amp;amp;&amp;amp; canMake[items - 1][weight - currentItemWeight]) {&lt;br /&gt;
                 canMake[items][weight] = true;&lt;br /&gt;
                 {{Changed|1=take[items][weight] = true;}}&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     {{Changed|1=if (!canMake[itemWeight.size()][targetItem])}}&lt;br /&gt;
         {{Changed|1=return {};}}&lt;br /&gt;
 &lt;br /&gt;
     {{Changed|1=vector&amp;lt;int&amp;gt; takenItemIndexes;}}&lt;br /&gt;
     {{Changed|1=for (int items = itemWeight.size(), weight = targetWeight; items &amp;gt; 0; items--) {}}&lt;br /&gt;
         {{Changed|1=if (take[items][weight]) {}}&lt;br /&gt;
             {{Changed|1=takenItemIndexes.push_back(items - 1);}}&lt;br /&gt;
             {{Changed|1=weight -= itemWeight[items - 1];}}&lt;br /&gt;
         {{Changed|1=} }}&lt;br /&gt;
     {{Changed|1=} }}&lt;br /&gt;
     {{Changed|1=return takenItemIndexes;}}&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Временная сложность решения Θ(NM). Для таблиц требуется Θ(NM) памяти.&lt;br /&gt;
&lt;br /&gt;
=== Номера предметов, дающих заданный вес, Θ(M) памяти (⋆) ===&lt;br /&gt;
&lt;br /&gt;
* [https://stackoverflow.com/questions/51941993/0-1-knapsack-find-solution-set-in-space-optimised-implementation stackoverflow.com/questions/51941993/0-1-knapsack-find-solution-set-in-space-optimised-implementation]&lt;br /&gt;
* [https://www.cs.colostate.edu/~cs575dl/Sp2015/Lectures/Knapsack.pdf www.cs.colostate.edu/~cs575dl/Sp2015/Lectures/Knapsack.pdf]&lt;br /&gt;
&lt;br /&gt;
 vector&amp;lt;bool&amp;gt; getCanMake(const vector&amp;lt;int&amp;gt; &amp;amp;itemWeight, int targetWeight, int l, int r) {&lt;br /&gt;
     vector&amp;lt;bool&amp;gt; canMake(targetWeight + 1);&lt;br /&gt;
 &lt;br /&gt;
     canMake[0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemIndex = l; itemIndex &amp;lt;= r; itemIndex++) {&lt;br /&gt;
         int currentItemWeight = itemWeight[itemIndex];&lt;br /&gt;
 &lt;br /&gt;
         for (int weight = currentItemWeight; weight &amp;lt;= targetWeight; weight++) {&lt;br /&gt;
             if (canMake[weight - currentItemWeight])&lt;br /&gt;
                 canMake[weight] = true;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return canMake;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 vector&amp;lt;int&amp;gt; solve(const vector&amp;lt;int&amp;gt; &amp;amp;itemWeight, int targetWeight, int l, int r) {&lt;br /&gt;
     if (l == r)&lt;br /&gt;
         return targetWeight == itemWeight[l] ? { l } : {};&lt;br /&gt;
 &lt;br /&gt;
     int m = l + (r - l) / 2;&lt;br /&gt;
     vector&amp;lt;bool&amp;gt; aCan = getCanMake(itemWeight, targetWeight, l, m);&lt;br /&gt;
     vector&amp;lt;bool&amp;gt; bCan = getCanMake(itemWeight, targetWeight, m + 1, r);&lt;br /&gt;
 &lt;br /&gt;
     int aWeight = 0, bWeight = targetWeight;&lt;br /&gt;
     while (!aCan[aWeight] || !bCan[bWeight]) {&lt;br /&gt;
         aWeight++;&lt;br /&gt;
         bWeight--;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;int&amp;gt; aItemIndexes = solve(itemWeight, aWeight, l, m);&lt;br /&gt;
     vector&amp;lt;int&amp;gt; bItemIndexes = solve(itemWeight, bWeight, m + 1, r);&lt;br /&gt;
     aItemIndexes.insert(aItemIndexes.end(), bIntemIndexes.begin(), bItemIndexes.end());&lt;br /&gt;
     return aItemIndexes;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Временная сложность решения Θ(NM). Для таблицы требуется Θ(M) памяти.&lt;br /&gt;
&lt;br /&gt;
== Вариации на тему задачи о сумме подмножеств ==&lt;br /&gt;
&lt;br /&gt;
=== Какой максимальный вес не больше заданного можно набрать ===&lt;br /&gt;
&lt;br /&gt;
Решаем задачу о сумме подмножеств, ищем самое правое true в последней строке таблицы. &lt;br /&gt;
&lt;br /&gt;
 {{Changed|int}} solve(const vector&amp;lt;int&amp;gt; &amp;amp;itemWeight, int targetWeight) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; canMake(itemWeight.size() + 1, vector&amp;lt;int&amp;gt;(targetWeight + 1));&lt;br /&gt;
 &lt;br /&gt;
     canMake[0][0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int items = 1; items &amp;lt;= itemWeight.size(); items++) {&lt;br /&gt;
         int currentItemWeight = itemWeight[items - 1];&lt;br /&gt;
 &lt;br /&gt;
         for (int weight = 0; weight &amp;lt;= targetWeight; weight++) {&lt;br /&gt;
             if (canMake[items - 1][weight])&lt;br /&gt;
                 canMake[items][weight] = true;&lt;br /&gt;
 &lt;br /&gt;
             if (weight &amp;gt;= currentItemWeight &amp;amp;&amp;amp; canMake[items - 1][weight - currentItemWeight])&lt;br /&gt;
                 canMake[items][weight] = true;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     {{Changed|1=while (!canMake[items.size()][targetWeight])}}&lt;br /&gt;
        {{Changed|1=targetWeight--;}}&lt;br /&gt;
     {{Changed|1=return targetWeight;}}&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Временная сложность решения Θ(NM). Для таблицы требуется Θ(NM) или Θ(M) памяти.&lt;br /&gt;
&lt;br /&gt;
=== Можно ли разделить предметы на два набора одинакового веса ===&lt;br /&gt;
&lt;br /&gt;
Решаем задачу о сумме подмножеств для targetWeight == (сумма весов предметов) / 2. &lt;br /&gt;
&lt;br /&gt;
 bool solve(const vector&amp;lt;int&amp;gt; &amp;amp;itemWeight) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; canMake(itemWeight.size() + 1, vector&amp;lt;int&amp;gt;(targetWeight + 1));&lt;br /&gt;
 &lt;br /&gt;
     {{Changed|1=int targetWeight = 0;}}&lt;br /&gt;
     {{Changed|1=for (int weight : itemWeight)}}&lt;br /&gt;
         {{Changed|1=targetWeight += weight;}}&lt;br /&gt;
     {{Changed|1=targetWeight /= 2;}}&lt;br /&gt;
 &lt;br /&gt;
     canMake[0][0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int items = 1; items &amp;lt;= itemWeight.size(); items++) {&lt;br /&gt;
         int currentItemWeight = itemWeight[items - 1];&lt;br /&gt;
 &lt;br /&gt;
         for (int weight = 0; weight &amp;lt;= targetWeight; weight++) {&lt;br /&gt;
             if (canMake[items - 1][weight])&lt;br /&gt;
                 canMake[items][weight] = true;&lt;br /&gt;
 &lt;br /&gt;
             if (weight &amp;gt;= currentItemWeight &amp;amp;&amp;amp; canMake[items - 1][weight - currentItemWeight])&lt;br /&gt;
                 canMake[items][weight] = true;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return canMake[items.size()][targetWeight];&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Временная сложность решения Θ(NM). Для таблицы требуется Θ(NM) или Θ(M) памяти.&lt;br /&gt;
&lt;br /&gt;
== Примеры кода ==&lt;br /&gt;
&lt;br /&gt;
=== Можно ли набрать сумму targetSum ===&lt;br /&gt;
&lt;br /&gt;
==== Базовый вариант ====&lt;br /&gt;
{|&lt;br /&gt;
|width=50%| Предмет каждого вида ровно один&lt;br /&gt;
| &amp;amp;nbsp;&lt;br /&gt;
|width=50%| Предметов каждого вида неограниченно много&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
 bool canMakeSum(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; canMake(items.size() + 1, vector&amp;lt;bool&amp;gt;(targetSum + 1));&lt;br /&gt;
     canMake[0][0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemCount = 1; itemCount &amp;lt;= items.size(); itemCount++) {&lt;br /&gt;
         int itemWeight = items[itemCount - 1];&lt;br /&gt;
         for (int sum = 0; sum &amp;lt;= targetSum; sum++)&lt;br /&gt;
             if (canMake[itemCount - 1][sum] ||&lt;br /&gt;
                 sum &amp;gt;= itemWeight &amp;amp;&amp;amp; canMake[itemCount - 1][sum - itemWeight])&lt;br /&gt;
                 canMake[itemCount][sum] = true;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return canMake[items.size()][targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
 bool canMakeSum(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; canMake(items.size() + 1, vector&amp;lt;bool&amp;gt;(targetSum + 1));&lt;br /&gt;
     canMake[0][0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemCount = 1; itemCount &amp;lt;= items.size(); itemCount++) {&lt;br /&gt;
         int itemWeight = items[itemCount - 1];&lt;br /&gt;
         for (int sum = 0; sum &amp;lt;= targetSum; sum++)&lt;br /&gt;
             if (canMake[itemCount - 1][sum] ||&lt;br /&gt;
                 sum &amp;gt;= itemWeight &amp;amp;&amp;amp; canMake[itemCount][sum - itemWeight])&lt;br /&gt;
                 canMake[itemCount][sum] = true;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return canMake[items.size()][targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Вернуть сертификат (список предметов) ====&lt;br /&gt;
{|&lt;br /&gt;
|width=50%| Предмет каждого вида ровно один&lt;br /&gt;
| &amp;amp;nbsp;&lt;br /&gt;
|width=50%| Предметов каждого вида неограниченно много&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
 vector&amp;lt;int&amp;gt; canMakeSum(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; canMake(items.size() + 1, vector&amp;lt;bool&amp;gt;(targetSum + 1));&lt;br /&gt;
     canMake[0][0] = true;&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;&amp;gt; from(items.size() + 1, vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;(targetSum + 1));&lt;br /&gt;
 &lt;br /&gt;
     for (int itemCount = 1; itemCount &amp;lt;= items.size(); itemCount++) {&lt;br /&gt;
         int itemWeight = items[itemCount - 1];&lt;br /&gt;
         for (int sum = 0; sum &amp;lt;= targetSum; sum++) {&lt;br /&gt;
             if (canMake[itemCount - 1][sum]) {&lt;br /&gt;
                 canMake[itemCount][sum] = true;&lt;br /&gt;
                 from[itemCount][sum] = { itemCount - 1, sum };&lt;br /&gt;
             } else if (sum &amp;gt;= itemWeight &amp;amp;&amp;amp; canMake[itemCount - 1][sum - itemWeight]) {&lt;br /&gt;
                 canMake[itemCount][sum] = true;&lt;br /&gt;
                 from[itemCount][sum] = { itemCount - 1, sum - itemWeight };&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     if (!canMake[items.size()][targetSum])&lt;br /&gt;
         return {};&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;int&amp;gt; takenItems;&lt;br /&gt;
     for (int itemCount = items.size(), sum = targetSum; itemCount &amp;gt; 0; ) {&lt;br /&gt;
         auto &amp;amp;[prevItemCount, prevSum] = from[itemCount][sum];&lt;br /&gt;
         if (prevSum != sum)&lt;br /&gt;
             takenItems.push_back(sum - prevSum);&lt;br /&gt;
         itemCount = prevItemCount;&lt;br /&gt;
         sum = prevSum;&lt;br /&gt;
     }&lt;br /&gt;
     return takenItems;&lt;br /&gt;
 }&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
 vector&amp;lt;int&amp;gt; canMakeSum(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;bool&amp;gt;&amp;gt; canMake(items.size() + 1, vector&amp;lt;bool&amp;gt;(targetSum + 1));&lt;br /&gt;
     canMake[0][0] = true;&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;&amp;gt; from(items.size() + 1, vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;(targetSum + 1));&lt;br /&gt;
 &lt;br /&gt;
     for (int itemCount = 1; itemCount &amp;lt;= items.size(); itemCount++) {&lt;br /&gt;
         int itemWeight = items[itemCount - 1];&lt;br /&gt;
         for (int sum = 0; sum &amp;lt;= targetSum; sum++) {&lt;br /&gt;
             if (canMake[itemCount - 1][sum]) {&lt;br /&gt;
                 canMake[itemCount][sum] = true;&lt;br /&gt;
                 from[itemCount][sum] = { itemCount - 1, sum };&lt;br /&gt;
             } else if (sum &amp;gt;= itemWeight &amp;amp;&amp;amp; canMake[itemCount][sum - itemWeight]) {&lt;br /&gt;
                 canMake[itemCount][sum] = true;&lt;br /&gt;
                 from[itemCount][sum] = { itemCount, sum - itemWeight };&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     if (!canMake[items.size()][targetSum])&lt;br /&gt;
         return {};&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;int&amp;gt; takenItems;&lt;br /&gt;
     for (int itemCount = items.size(), sum = targetSum; itemCount &amp;gt; 0; ) {&lt;br /&gt;
         auto &amp;amp;[prevItemCount, prevSum] = from[itemCount][sum];&lt;br /&gt;
         if (prevSum != sum)&lt;br /&gt;
             takenItems.push_back(sum - prevSum);&lt;br /&gt;
         itemCount = prevItemCount;&lt;br /&gt;
         sum = prevSum;&lt;br /&gt;
     }&lt;br /&gt;
     return takenItems;&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Базовый вариант, O(targetSum) памяти ====&lt;br /&gt;
{|&lt;br /&gt;
|width=50%| Предмет каждого вида ровно один&lt;br /&gt;
| &amp;amp;nbsp;&lt;br /&gt;
|width=50%| Предметов каждого вида неограниченно много&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
 bool canMakeSum(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;bool&amp;gt; canMake(targetSum + 1);&lt;br /&gt;
     canMake[0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemWeight : items)&lt;br /&gt;
         for (int sum = targetSum; sum &amp;gt;= itemWeight; sum--)&lt;br /&gt;
             if (canMake[sum - itemWeight])&lt;br /&gt;
                 canMake[sum] = true;&lt;br /&gt;
 &lt;br /&gt;
     return canMake[targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
 bool canMakeSum(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;bool&amp;gt; canMake(targetSum + 1);&lt;br /&gt;
     canMake[0] = true;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemWeight : items)&lt;br /&gt;
         for (int sum = itemWeight; sum &amp;lt;= targetSum; sum++)&lt;br /&gt;
             if (canMake[sum - itemWeight])&lt;br /&gt;
                 canMake[sum] = true;&lt;br /&gt;
 &lt;br /&gt;
     return canMake[targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Вернуть сертификат (список предметов), O(targetSum) памяти ====&lt;br /&gt;
{|&lt;br /&gt;
|width=50%| Предмет каждого вида ровно один&lt;br /&gt;
| &amp;amp;nbsp;&lt;br /&gt;
|width=50%| Предметов каждого вида неограниченно много&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
 vector&amp;lt;int&amp;gt; canMakeSum(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;bool&amp;gt; canMake(targetSum + 1);&lt;br /&gt;
     canMake[0] = true;&lt;br /&gt;
     vector&amp;lt;int&amp;gt; from(targetSum + 1);&lt;br /&gt;
 &lt;br /&gt;
     for (int itemWeight : items) {&lt;br /&gt;
         for (int sum = targetSum; sum &amp;gt;= itemWeight; sum--) {&lt;br /&gt;
             if (!canMake[sum] &amp;amp;&amp;amp; canMake[sum - itemWeight]) {&lt;br /&gt;
                 canMake[sum] = true;&lt;br /&gt;
                 from[sum] = sum - itemWeight;&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     if (!canMake[targetSum])&lt;br /&gt;
         return {};&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;int&amp;gt; takenItems;&lt;br /&gt;
     for (int sum = targetSum; sum &amp;gt; 0; sum = from[sum])&lt;br /&gt;
         takenItems.push_back(sum - from[sum]);&lt;br /&gt;
     return takenItems;&lt;br /&gt;
 }&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
 vector&amp;lt;int&amp;gt; canMakeSum(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;bool&amp;gt; canMake(targetSum + 1);&lt;br /&gt;
     canMake[0] = true;&lt;br /&gt;
     vector&amp;lt;int&amp;gt; from(targetSum + 1);&lt;br /&gt;
 &lt;br /&gt;
     for (int itemWeight : items) {&lt;br /&gt;
         for (int sum = itemWeight; sum &amp;lt;= targetSum; sum++) {&lt;br /&gt;
             if (!canMake[sum] &amp;amp;&amp;amp; canMake[sum - itemWeight]) {&lt;br /&gt;
                 canMake[sum] = true;&lt;br /&gt;
                 from[sum] = sum - itemWeight;&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     if (!canMake[targetSum])&lt;br /&gt;
         return {};&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;int&amp;gt; takenItems;&lt;br /&gt;
     for (int sum = targetSum; sum &amp;gt; 0; sum = from[sum])&lt;br /&gt;
         takenItems.push_back(sum - from[sum]);&lt;br /&gt;
     return takenItems;&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Каким минимальным количеством предметом можно набрать сумму targetSum ===&lt;br /&gt;
&lt;br /&gt;
==== Базовый вариант ====&lt;br /&gt;
{|&lt;br /&gt;
|width=50%| Предмет каждого вида ровно один&lt;br /&gt;
| &amp;amp;nbsp;&lt;br /&gt;
|width=50%| Предметов каждого вида неограниченно много&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
 int getMinCount(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; minCount(items.size() + 1, vector&amp;lt;int&amp;gt;(targetSum + 1, 1e9));&lt;br /&gt;
     minCount[0][0] = 0;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemCount = 1; itemCount &amp;lt;= items.size(); itemCount++) {&lt;br /&gt;
         int itemWeight = items[itemCount - 1];&lt;br /&gt;
         for (int sum = 0; sum &amp;lt;= targetSum; sum++) {&lt;br /&gt;
             if (minCount[itemCount][sum] &amp;gt; minCount[itemCount - 1][sum])&lt;br /&gt;
                 minCount[itemCount][sum] = minCount[itemCount - 1][sum];&lt;br /&gt;
             if (sum &amp;gt;= itemWeight &amp;amp;&amp;amp; minCount[itemCount][sum] &amp;gt; minCount[itemCount - 1][sum - itemWeight] + 1)&lt;br /&gt;
                 minCount[itemCount][sum] = minCount[itemCount - 1][sum - itemWeight] + 1;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return minCount[items.size()][targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
 int getMinCount(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; minCount(items.size() + 1, vector&amp;lt;int&amp;gt;(targetSum + 1, 1e9));&lt;br /&gt;
     minCount[0][0] = 0;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemCount = 1; itemCount &amp;lt;= items.size(); itemCount++) {&lt;br /&gt;
         int itemWeight = items[itemCount - 1];&lt;br /&gt;
         for (int sum = 0; sum &amp;lt;= targetSum; sum++) {&lt;br /&gt;
             if (minCount[itemCount][sum] &amp;gt; minCount[itemCount - 1][sum])&lt;br /&gt;
                 minCount[itemCount][sum] = minCount[itemCount - 1][sum];&lt;br /&gt;
             if (sum &amp;gt;= itemWeight &amp;amp;&amp;amp; minCount[itemCount][sum] &amp;gt; minCount[itemCount][sum - itemWeight] + 1)&lt;br /&gt;
                 minCount[itemCount][sum] = minCount[itemCount][sum - itemWeight] + 1;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return minCount[items.size()][targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Вернуть сертификат (список предметов) ====&lt;br /&gt;
{|&lt;br /&gt;
|width=50%| Предмет каждого вида ровно один&lt;br /&gt;
| &amp;amp;nbsp;&lt;br /&gt;
|width=50%| Предметов каждого вида неограниченно много&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
 vector&amp;lt;int&amp;gt; getMinCount(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; minCount(items.size() + 1, vector&amp;lt;int&amp;gt;(targetSum + 1, 1e9));&lt;br /&gt;
     minCount[0][0] = 0;&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;&amp;gt; from(items.size() + 1, vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;(targetSum + 1));&lt;br /&gt;
 &lt;br /&gt;
     for (int itemCount = 1; itemCount &amp;lt;= items.size(); itemCount++) {&lt;br /&gt;
         int itemWeight = items[itemCount - 1];&lt;br /&gt;
         for (int sum = 0; sum &amp;lt;= targetSum; sum++) {&lt;br /&gt;
             if (minCount[itemCount][sum] &amp;gt; minCount[itemCount - 1][sum]) {&lt;br /&gt;
                 minCount[itemCount][sum] = minCount[itemCount - 1][sum];&lt;br /&gt;
                 from[itemCount][sum] = { itemCount - 1, sum };&lt;br /&gt;
             }&lt;br /&gt;
             if (sum &amp;gt;= itemWeight &amp;amp;&amp;amp; minCount[itemCount][sum] &amp;gt; minCount[itemCount - 1][sum - itemWeight] + 1) {&lt;br /&gt;
                 minCount[itemCount][sum] = minCount[itemCount - 1][sum - itemWeight] + 1;&lt;br /&gt;
                 from[itemCount][sum] = { itemCount - 1, sum - itemWeight };&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     if (minCount[items.size()][targetSum] == 1e9)&lt;br /&gt;
         return {};&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;int&amp;gt; takenItems;&lt;br /&gt;
     for (int itemCount = items.size(), sum = targetSum; itemCount &amp;gt; 0; ) {&lt;br /&gt;
         auto &amp;amp;[prevItemCount, prevSum] = from[itemCount][sum];&lt;br /&gt;
         if (prevSum != sum)&lt;br /&gt;
             takenItems.push_back(sum - prevSum);&lt;br /&gt;
         itemCount = prevItemCount;&lt;br /&gt;
         sum = prevSum;&lt;br /&gt;
     }&lt;br /&gt;
     return takenItems;&lt;br /&gt;
 }&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
 vector&amp;lt;int&amp;gt; getMinCount(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; minCount(items.size() + 1, vector&amp;lt;int&amp;gt;(targetSum + 1, 1e9));&lt;br /&gt;
     minCount[0][0] = 0;&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;&amp;gt; from(items.size() + 1, vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;(targetSum + 1));&lt;br /&gt;
 &lt;br /&gt;
     for (int itemCount = 1; itemCount &amp;lt;= items.size(); itemCount++) {&lt;br /&gt;
         int itemWeight = items[itemCount - 1];&lt;br /&gt;
         for (int sum = 0; sum &amp;lt;= targetSum; sum++) {&lt;br /&gt;
             if (minCount[itemCount][sum] &amp;gt; minCount[itemCount - 1][sum]) {&lt;br /&gt;
                 minCount[itemCount][sum] = minCount[itemCount - 1][sum];&lt;br /&gt;
                 from[itemCount][sum] = { itemCount - 1, sum };&lt;br /&gt;
             }&lt;br /&gt;
             if (sum &amp;gt;= itemWeight &amp;amp;&amp;amp; minCount[itemCount][sum] &amp;gt; minCount[itemCount][sum - itemWeight] + 1) {&lt;br /&gt;
                 minCount[itemCount][sum] = minCount[itemCount][sum - itemWeight] + 1;&lt;br /&gt;
                 from[itemCount][sum] = { itemCount, sum - itemWeight };&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     if (minCount[items.size()][targetSum] == 1e9)&lt;br /&gt;
         return {};&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;int&amp;gt; takenItems;&lt;br /&gt;
     for (int itemCount = items.size(), sum = targetSum; itemCount &amp;gt; 0; ) {&lt;br /&gt;
         auto &amp;amp;[prevItemCount, prevSum] = from[itemCount][sum];&lt;br /&gt;
         if (prevSum != sum)&lt;br /&gt;
             takenItems.push_back(sum - prevSum);&lt;br /&gt;
         itemCount = prevItemCount;&lt;br /&gt;
         sum = prevSum;&lt;br /&gt;
     }&lt;br /&gt;
     return takenItems;&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Базовый вариант, O(targetSum) памяти ====&lt;br /&gt;
{|&lt;br /&gt;
|width=50%| Предмет каждого вида ровно один&lt;br /&gt;
| &amp;amp;nbsp;&lt;br /&gt;
|width=50%| Предметов каждого вида неограниченно много&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
 int getMinCount(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;int&amp;gt; minCount(targetSum + 1, 1e9);&lt;br /&gt;
     minCount[0] = 0;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemWeight : items)&lt;br /&gt;
         for (int sum = targetSum; sum &amp;gt;= itemWeight; sum--)&lt;br /&gt;
             if (minCount[sum] &amp;gt; minCount[sum - itemWeight] + 1)&lt;br /&gt;
                 minCount[sum] = minCount[sum - itemWeight] + 1;&lt;br /&gt;
 &lt;br /&gt;
     return minCount[targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
 int getMinCount(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;int&amp;gt; minCount(targetSum + 1, 1e9);&lt;br /&gt;
     minCount[0] = 0;&lt;br /&gt;
 &lt;br /&gt;
     for (int itemWeight : items)&lt;br /&gt;
         for (int sum = itemWeight; sum &amp;lt;= targetSum; sum++)&lt;br /&gt;
             if (minCount[sum] &amp;gt; minCount[sum - itemWeight] + 1)&lt;br /&gt;
                 minCount[sum] = minCount[sum - itemWeight] + 1;&lt;br /&gt;
 &lt;br /&gt;
     return minCount[targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Вернуть сертификат (список предметов), O(targetSum) памяти ====&lt;br /&gt;
{|&lt;br /&gt;
|width=50%| Предмет каждого вида ровно один&lt;br /&gt;
| &amp;amp;nbsp;&lt;br /&gt;
|width=50%| Предметов каждого вида неограниченно много&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
(Невозможно. Контртест: items = { 1, 2, 3 }, targetSum = 6. Ожидается ответ { 1, 2, 3 }, будет получен ответ { 3, 3 }.)&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
 vector&amp;lt;int&amp;gt; getMinCount(vector&amp;lt;int&amp;gt; &amp;amp;items, int targetSum) {&lt;br /&gt;
     vector&amp;lt;int&amp;gt; minCount(targetSum + 1, 1e9);&lt;br /&gt;
     minCount[0] = 0;&lt;br /&gt;
     vector&amp;lt;int&amp;gt; from(targetSum + 1);&lt;br /&gt;
 &lt;br /&gt;
     for (int itemWeight : items) {&lt;br /&gt;
         for (int sum = itemWeight; sum &amp;lt;= targetSum; sum++) {&lt;br /&gt;
             if (minCount[sum] &amp;gt; minCount[sum - itemWeight] + 1) {&lt;br /&gt;
                 minCount[sum] = minCount[sum - itemWeight] + 1;&lt;br /&gt;
                 from[sum] = sum - itemWeight;&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     if (minCount[targetSum] == 1e9)&lt;br /&gt;
         return {};&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;int&amp;gt; takenItems;&lt;br /&gt;
     for (int sum = targetSum; sum &amp;gt; 0; sum = from[sum])&lt;br /&gt;
         takenItems.push_back(sum - from[sum]);&lt;br /&gt;
     return takenItems;&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Сколько есть способов монетами coins набрать сумму targetSum? Способы, отличающиеся только порядком монет, считаются одинаковыми.&lt;br /&gt;
{|&lt;br /&gt;
|width=50%| Есть лишь одна монета каждого вида &lt;br /&gt;
| &amp;amp;nbsp;&lt;br /&gt;
|width=50%| Монет каждого вида неограниченно много&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
 long long solve(const vector&amp;lt;int&amp;gt; &amp;amp;coins, int targetSum) {&lt;br /&gt;
     vector&amp;lt;long long&amp;gt; ways(targetSum + 1);&lt;br /&gt;
     ways[0] = 1;&lt;br /&gt;
 &lt;br /&gt;
     for (int coin : coins)&lt;br /&gt;
         for (int sum = targetSum; sum &amp;gt;= coin; sum--)&lt;br /&gt;
             ways[sum] += ways[sum - coin];&lt;br /&gt;
 &lt;br /&gt;
     cout &amp;lt;&amp;lt; ways[targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
 long long solve(const vector&amp;lt;int&amp;gt; &amp;amp;coins, int targetSum) {&lt;br /&gt;
     vector&amp;lt;long long&amp;gt; ways(targetSum + 1);&lt;br /&gt;
     ways[0] = 1;&lt;br /&gt;
 &lt;br /&gt;
     for (int coin : coins)&lt;br /&gt;
         for (int sum = coin; sum &amp;lt;= targetSum; sum++)&lt;br /&gt;
             ways[sum] += ways[sum - coin];&lt;br /&gt;
 &lt;br /&gt;
     cout &amp;lt;&amp;lt; ways[targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Сколько есть способов монетами coins набрать сумму targetSum? Способы, отличающиеся только порядком монет, считаются различными.&lt;br /&gt;
{|&lt;br /&gt;
|width=50%| Есть лишь одна монета каждого вида &lt;br /&gt;
| &amp;amp;nbsp;&lt;br /&gt;
|width=50%| Монет каждого вида неограниченно много&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
 long long solve(const vector&amp;lt;int&amp;gt; &amp;amp;coins, int targetSum) {&lt;br /&gt;
     vector&amp;lt;long long&amp;gt; ways(targetSum + 1);&lt;br /&gt;
     ways[0] = 1;&lt;br /&gt;
 &lt;br /&gt;
     for (int sum = 1; sum &amp;lt;= targetSum; sum++)&lt;br /&gt;
         for (int coin : coins)&lt;br /&gt;
             if (coin &amp;lt;= sum)&lt;br /&gt;
                 ways[sum] += [sum - coin];&lt;br /&gt;
 &lt;br /&gt;
     cout &amp;lt;&amp;lt; ways[targetSum];&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
* [http://algorithmica.org/tg/knapsack-gis-gcs algorithmica.org — Задача о рюкзаке, НВП и НОП]&lt;br /&gt;
* [https://brilliant.org/wiki/backpack-problem/ brilliant.org — Backpack Problem]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Динамическое программирование]]&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%94%D0%B8%D0%BD%D0%B8%D1%86%D0%B0&amp;diff=2934</id>
		<title>Алгоритм Диница</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%94%D0%B8%D0%BD%D0%B8%D1%86%D0%B0&amp;diff=2934"/>
		<updated>2025-10-22T19:51:47Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt; class Graph {&lt;br /&gt;
     struct Edge {&lt;br /&gt;
         int a, b, capacity, flow = 0;&lt;br /&gt;
 &lt;br /&gt;
         Edge(int a, int b, int capacity) :&lt;br /&gt;
             a(a), b(b), capacity(capacity) {}&lt;br /&gt;
 &lt;br /&gt;
         int other(int v) const {&lt;br /&gt;
             return v == b ? a : b;&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         int capacityTo(int v) const {&lt;br /&gt;
             return v == b ? capacity - flow : flow;&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         void addFlowTo(int v, int deltaFlow) {&lt;br /&gt;
             flow += (v == b ? deltaFlow : -deltaFlow);&lt;br /&gt;
         }&lt;br /&gt;
     };&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;Edge&amp;gt; edges;&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; graph;&lt;br /&gt;
     vector&amp;lt;int&amp;gt; dist, edgeTo, index;&lt;br /&gt;
 &lt;br /&gt;
     bool hasPath(int start, int finish) {&lt;br /&gt;
         dist.assign(graph.size(), 1e9);&lt;br /&gt;
         edgeTo.assign(graph.size(), -1);&lt;br /&gt;
         index.assign(graph.size(), 0);&lt;br /&gt;
         queue&amp;lt;int&amp;gt; q;&lt;br /&gt;
 &lt;br /&gt;
         dist[start] = 0;&lt;br /&gt;
         q.push(start);&lt;br /&gt;
 &lt;br /&gt;
         while (!q.empty()) {&lt;br /&gt;
             int v = q.front();&lt;br /&gt;
             q.pop();&lt;br /&gt;
 &lt;br /&gt;
             for (int edgeIndex : graph[v]) {&lt;br /&gt;
                 int to = edges[edgeIndex].other(v);&lt;br /&gt;
                 if (dist[to] &amp;gt; dist[v] + 1 &amp;amp;&amp;amp; edges[edgeIndex].capacityTo(to)) {&lt;br /&gt;
                     dist[to] = dist[v] + 1;&lt;br /&gt;
                     q.push(to);&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         return dist[finish] != 1e9;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     bool dfs(int v, int finish) {&lt;br /&gt;
         if (v == finish)&lt;br /&gt;
             return 1;&lt;br /&gt;
 &lt;br /&gt;
         for (; index[v] &amp;lt; graph[v].size(); index[v]++) {&lt;br /&gt;
             int edgeIndex = graph[v][index[v]], to = edges[edgeIndex].other(v);&lt;br /&gt;
             if (dist[to] == dist[v] + 1 &amp;amp;&amp;amp; edges[edgeIndex].capacityTo(to) &amp;amp;&amp;amp; dfs(to, finish)) {&lt;br /&gt;
                 edgeTo[to] = edgeIndex;&lt;br /&gt;
                 return 1;&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         return 0;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     int getMinCapacity(int start, int finish) {&lt;br /&gt;
         int minCapacity = 1e9;&lt;br /&gt;
         for (int v = finish; v != start; v = edges[edgeTo[v]].other(v))&lt;br /&gt;
             minCapacity = min(minCapacity, edges[edgeTo[v]].capacityTo(v));&lt;br /&gt;
         return minCapacity;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     void addFlow(int start, int finish, int deltaFlow) {&lt;br /&gt;
         for (int v = finish; v != start; v = edges[edgeTo[v]].other(v))&lt;br /&gt;
             edges[edgeTo[v]].addFlowTo(v, deltaFlow);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
 public:&lt;br /&gt;
     Graph(int vertexCount) : graph(vertexCount) {}&lt;br /&gt;
 &lt;br /&gt;
     void addEdge(int from, int to, int capacity) {&lt;br /&gt;
         edges.push_back(Edge(from, to, capacity));&lt;br /&gt;
         graph[from].push_back(edges.size() - 1);&lt;br /&gt;
         graph[to].push_back(edges.size() - 1);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     long long maxFlow(int start, int finish) {&lt;br /&gt;
         long long flow = 0;&lt;br /&gt;
         while (hasPath(start, finish)) {&lt;br /&gt;
             while (dfs(start, finish)) {&lt;br /&gt;
                 int deltaFlow = getMinCapacity(start, finish);&lt;br /&gt;
                 addFlow(start, finish, deltaFlow);&lt;br /&gt;
                 flow += deltaFlow;&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         return flow;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
Теория:&lt;br /&gt;
* [http://e-maxx.ru/algo/dinic e-maxx.ru — Алгоритм Диница нахождения максимального потока]&lt;br /&gt;
* [https://cp-algorithms.com/graph/dinic.html cp-algorithms.com — Maximum flow — Dinic&#039;s algorithm]&lt;br /&gt;
* [http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D1%85%D0%B5%D0%BC%D0%B0_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0_%D0%94%D0%B8%D0%BD%D0%B8%D1%86%D0%B0 neerc.ifmo.ru/wiki — Схема алгоритма Диница]&lt;br /&gt;
* [https://wiki.algocode.ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%94%D0%B8%D0%BD%D0%B8%D1%86%D0%B0 wiki.algocode.ru — Алгоритм Диница]&lt;br /&gt;
* [https://codeforces.com/blog/entry/104960 codeforces.com — My way of understanding Dinic&#039;s algorithm]&lt;br /&gt;
* [https://codeforces.com/blog/entry/145343 codeforces.com — Worst-Case Graphs for Maximum Flow Algorithms]&lt;br /&gt;
* [https://usaco.guide/adv/max-flow?lang=cpp#dinics-algorithm usaco.guide — Dinic&#039;s Algorithm]&lt;br /&gt;
Демонстрация:&lt;br /&gt;
* [https://visualgo.net/en/maxflow visualgo.net — Network Flow]&lt;br /&gt;
Код:&lt;br /&gt;
* [https://github.com/indy256/codelibrary/blob/master/cpp/graphs/flows/max_flow_dinic.h indy256/codelibrary/cpp/graphs/flows/max_flow_dinic.h]&lt;br /&gt;
* [https://github.com/ADJA/algos/blob/master/Graphs/Dinic.cpp ADJA/algos/Graphs/Dinic.cpp]&lt;br /&gt;
Задачи:&lt;br /&gt;
* [http://informatics.mccme.ru/course/view.php?id=6 informatics.mccme.ru &amp;amp;mdash; Курс &amp;amp;laquo;Алгоритмы на графах&amp;amp;raquo; &amp;amp;mdash; часть 9]&lt;br /&gt;
&lt;br /&gt;
[[Category: Максимальный поток]]&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%AD%D0%B4%D0%BC%D0%BE%D0%BD%D0%B4%D1%81%D0%B0-%D0%9A%D0%B0%D1%80%D0%BF%D0%B0&amp;diff=2933</id>
		<title>Алгоритм Эдмондса-Карпа</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%AD%D0%B4%D0%BC%D0%BE%D0%BD%D0%B4%D1%81%D0%B0-%D0%9A%D0%B0%D1%80%D0%BF%D0%B0&amp;diff=2933"/>
		<updated>2025-10-22T19:47:53Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt; class Graph {&lt;br /&gt;
     struct Edge {&lt;br /&gt;
         int a, b, capacity, flow = 0;&lt;br /&gt;
 &lt;br /&gt;
         Edge(int a, int b, int capacity) :&lt;br /&gt;
             a(a), b(b), capacity(capacity) {}&lt;br /&gt;
 &lt;br /&gt;
         int other(int v) const {&lt;br /&gt;
             return v == b ? a : b;&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         int capacityTo(int v) const {&lt;br /&gt;
             return v == b ? capacity - flow : flow;&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         void addFlowTo(int v, int deltaFlow) {&lt;br /&gt;
             flow += (v == b ? deltaFlow : -deltaFlow);&lt;br /&gt;
         }&lt;br /&gt;
     };&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;Edge&amp;gt; edges;&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; graph;&lt;br /&gt;
     vector&amp;lt;int&amp;gt; visited, edgeTo;&lt;br /&gt;
 &lt;br /&gt;
     bool hasPath(int start, int finish) {&lt;br /&gt;
         visited.assign(graph.size(), 0);&lt;br /&gt;
         edgeTo.assign(graph.size(), -1);&lt;br /&gt;
         queue&amp;lt;int&amp;gt; q;&lt;br /&gt;
 &lt;br /&gt;
         visited[start] = 1;&lt;br /&gt;
         q.push(start);&lt;br /&gt;
 &lt;br /&gt;
         while (!q.empty()) {&lt;br /&gt;
             int v = q.front();&lt;br /&gt;
             q.pop();&lt;br /&gt;
 &lt;br /&gt;
             for (int edgeIndex : graph[v]) {&lt;br /&gt;
                 int to = edges[edgeIndex].other(v);&lt;br /&gt;
                 if (!visited[to] &amp;amp;&amp;amp; edges[edgeIndex].capacityTo(to)) {&lt;br /&gt;
                     visited[to] = 1;&lt;br /&gt;
                     edgeTo[to] = edgeIndex;&lt;br /&gt;
                     q.push(to);&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         return visited[finish];&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     int getMinCapacity(int start, int finish) {&lt;br /&gt;
         int minCapacity = 1e9;&lt;br /&gt;
         for (int v = finish; v != start; v = edges[edgeTo[v]].other(v))&lt;br /&gt;
             minCapacity = min(minCapacity, edges[edgeTo[v]].capacityTo(v));&lt;br /&gt;
         return minCapacity;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     void addFlow(int start, int finish, int deltaFlow) {&lt;br /&gt;
         for (int v = finish; v != start; v = edges[edgeTo[v]].other(v))&lt;br /&gt;
             edges[edgeTo[v]].addFlowTo(v, deltaFlow);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
 public:&lt;br /&gt;
     Graph(int vertexCount) : graph(vertexCount) {}&lt;br /&gt;
 &lt;br /&gt;
     void addEdge(int from, int to, int capacity) {&lt;br /&gt;
         edges.push_back(Edge(from, to, capacity));&lt;br /&gt;
         graph[from].push_back(edges.size() - 1);&lt;br /&gt;
         graph[to].push_back(edges.size() - 1);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     long long maxFlow(int start, int finish) {&lt;br /&gt;
         long long flow = 0;&lt;br /&gt;
         while (hasPath(start, finish)) {&lt;br /&gt;
             int deltaFlow = getMinCapacity(start, finish);&lt;br /&gt;
             addFlow(start, finish, deltaFlow);&lt;br /&gt;
             flow += deltaFlow;&lt;br /&gt;
         }&lt;br /&gt;
         return flow;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
Теория:&lt;br /&gt;
* [https://algs4.cs.princeton.edu/lectures/keynote/64MaxFlow.pdf algs4.cs.princeton.edu — 6.4 Maximum Flow]&lt;br /&gt;
* [http://e-maxx.ru/algo/edmonds_karp e-maxx.ru — Алгоритм Эдмондса-Карпа нахождения максимального потока за O (NM^2)]&lt;br /&gt;
* [http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%AD%D0%B4%D0%BC%D0%BE%D0%BD%D0%B4%D1%81%D0%B0-%D0%9A%D0%B0%D1%80%D0%BF%D0%B0 neerc.ifmo.ru/wiki — Алгоритм Эдмондса-Карпа]&lt;br /&gt;
* [http://brilliant.org/wiki/edmonds-karp-algorithm Brilliant.org — Edmonds-Karp Algorithm]&lt;br /&gt;
* [https://codeforces.com/blog/entry/145343 codeforces.com — Worst-Case Graphs for Maximum Flow Algorithms]&lt;br /&gt;
Демонстрация:&lt;br /&gt;
* [https://visualgo.net/en/maxflow VisuAlgo — Network Flow]&lt;br /&gt;
Код:&lt;br /&gt;
* [https://github.com/indy256/codelibrary/blob/master/java/graphs/flows/MaxFlowEdmondsKarp.java CodeLibrary &amp;amp;mdash; Maximum flow. Edmonds-Karp algorithm in O(min(E^2 * V, E * FLOW))]&lt;br /&gt;
* algs4.cs.princeton.edu/code &amp;amp;mdash; [http://algs4.cs.princeton.edu/code/edu/princeton/cs/algs4/FlowEdge.java.html capacitated edge with flow], [http://algs4.cs.princeton.edu/code/edu/princeton/cs/algs4/FlowNetwork.java.html capacitated network], [http://algs4.cs.princeton.edu/code/edu/princeton/cs/algs4/FordFulkerson.java.html maxflow–mincut] (несмотря на название, используется алгоритм Эдмондса-Карпа)&lt;br /&gt;
Задачи:&lt;br /&gt;
* [http://informatics.mccme.ru/course/view.php?id=6 informatics.mccme.ru &amp;amp;mdash; Курс &amp;amp;laquo;Алгоритмы на графах&amp;amp;raquo; &amp;amp;mdash; часть 9]&lt;br /&gt;
* [[:Категория:Задачи: Максимальный поток|Задачи: Максимальный поток]]&lt;br /&gt;
&lt;br /&gt;
[[Category: Максимальный поток]]&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A4%D0%BE%D1%80%D0%B4%D0%B0-%D0%A4%D0%B0%D0%BB%D0%BA%D0%B5%D1%80%D1%81%D0%BE%D0%BD%D0%B0&amp;diff=2932</id>
		<title>Алгоритм Форда-Фалкерсона</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A4%D0%BE%D1%80%D0%B4%D0%B0-%D0%A4%D0%B0%D0%BB%D0%BA%D0%B5%D1%80%D1%81%D0%BE%D0%BD%D0%B0&amp;diff=2932"/>
		<updated>2025-10-22T19:45:30Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt; class Graph {&lt;br /&gt;
     struct Edge {&lt;br /&gt;
         int a, b, capacity, flow = 0;&lt;br /&gt;
 &lt;br /&gt;
         Edge(int a, int b, int capacity) :&lt;br /&gt;
             a(a), b(b), capacity(capacity) {}&lt;br /&gt;
 &lt;br /&gt;
         int other(int v) const {&lt;br /&gt;
             return v == b ? a : b;&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         int capacityTo(int v) const {&lt;br /&gt;
             return v == b ? capacity - flow : flow;&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         void addFlowTo(int v, int deltaFlow) {&lt;br /&gt;
             flow += (v == b ? deltaFlow : -deltaFlow);&lt;br /&gt;
         }&lt;br /&gt;
     };&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;Edge&amp;gt; edges;&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; graph;&lt;br /&gt;
     vector&amp;lt;int&amp;gt; visited, edgeTo;&lt;br /&gt;
 &lt;br /&gt;
     void dfs(int v) {&lt;br /&gt;
         visited[v] = 1;&lt;br /&gt;
         for (int edgeIndex : graph[v]) {&lt;br /&gt;
             int to = edges[edgeIndex].other(v);&lt;br /&gt;
             if (!visited[to] &amp;amp;&amp;amp; edges[edgeIndex].capacityTo(to)) {&lt;br /&gt;
                 edgeTo[to] = edgeIndex;&lt;br /&gt;
                 dfs(to);&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     bool hasPath(int start, int finish) {&lt;br /&gt;
         visited.assign(graph.size(), 0);&lt;br /&gt;
         edgeTo.assign(graph.size(), -1);&lt;br /&gt;
         dfs(start);&lt;br /&gt;
         return visited[finish];&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     int getMinCapacity(int start, int finish) {&lt;br /&gt;
         int minCapacity = 1e9;&lt;br /&gt;
         for (int v = finish; v != start; v = edges[edgeTo[v]].other(v))&lt;br /&gt;
             minCapacity = min(minCapacity, edges[edgeTo[v]].capacityTo(v));&lt;br /&gt;
         return minCapacity;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     void addFlow(int start, int finish, int deltaFlow) {&lt;br /&gt;
         for (int v = finish; v != start; v = edges[edgeTo[v]].other(v))&lt;br /&gt;
             edges[edgeTo[v]].addFlowTo(v, deltaFlow);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
 public:&lt;br /&gt;
     Graph(int vertexCount) : graph(vertexCount) {}&lt;br /&gt;
 &lt;br /&gt;
     void addEdge(int from, int to, int capacity) {&lt;br /&gt;
         edges.push_back(Edge(from, to, capacity));&lt;br /&gt;
         graph[from].push_back(edges.size() - 1);&lt;br /&gt;
         graph[to].push_back(edges.size() - 1);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     long long maxFlow(int start, int finish) {&lt;br /&gt;
         long long flow = 0;&lt;br /&gt;
         while (hasPath(start, finish)) {&lt;br /&gt;
             int deltaFlow = getMinCapacity(start, finish);&lt;br /&gt;
             addFlow(start, finish, deltaFlow);&lt;br /&gt;
             flow += deltaFlow;&lt;br /&gt;
         }&lt;br /&gt;
         return flow;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
Теория:&lt;br /&gt;
* [https://algs4.cs.princeton.edu/lectures/keynote/64MaxFlow.pdf algs4.cs.princeton.edu — 6.4 Maximum Flow]&lt;br /&gt;
* [http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A4%D0%BE%D1%80%D0%B4%D0%B0-%D0%A4%D0%B0%D0%BB%D0%BA%D0%B5%D1%80%D1%81%D0%BE%D0%BD%D0%B0,_%D1%80%D0%B5%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F_%D1%81_%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D1%8C%D1%8E_%D0%BF%D0%BE%D0%B8%D1%81%D0%BA%D0%B0_%D0%B2_%D0%B3%D0%BB%D1%83%D0%B1%D0%B8%D0%BD%D1%83 neerc.ifmo.ru/wiki — Алгоритм Форда-Фалкерсона]&lt;br /&gt;
* [http://brilliant.org/wiki/ford-fulkerson-algorithm Brilliant.org — Ford-Fulkerson Algorithm]&lt;br /&gt;
* [https://codeforces.com/blog/entry/145343 codeforces.com — Worst-Case Graphs for Maximum Flow Algorithms]&lt;br /&gt;
Демонстрация:&lt;br /&gt;
* [https://visualgo.net/en/maxflow VisuAlgo — Network Flow]&lt;br /&gt;
Код:&lt;br /&gt;
* [https://github.com/indy256/codelibrary/blob/master/java/graphs/flows/MaxFlowFordFulkerson.java CodeLibrary — Maximum flow. Ford-Fulkerson alogithm in O(V^2 * FLOW)]&lt;br /&gt;
* [http://github.com/ADJA/algos/blob/master/Graphs/FordFulkerson.cpp Algos &amp;amp;mdash; Ford-Fulkerson maxflow]&lt;br /&gt;
* algs4.cs.princeton.edu/code &amp;amp;mdash; [http://algs4.cs.princeton.edu/code/edu/princeton/cs/algs4/FlowEdge.java.html capacitated edge with flow], [http://algs4.cs.princeton.edu/code/edu/princeton/cs/algs4/FlowNetwork.java.html capacitated network], [http://algs4.cs.princeton.edu/code/edu/princeton/cs/algs4/FordFulkerson.java.html maxflow–mincut] (несмотря на название, используется алгоритм Эдмондса-Карпа)&lt;br /&gt;
Задачи:&lt;br /&gt;
* [http://informatics.mccme.ru/course/view.php?id=6 informatics.mccme.ru &amp;amp;mdash; Курс &amp;amp;laquo;Алгоритмы на графах&amp;amp;raquo; &amp;amp;mdash; часть 9]&lt;br /&gt;
* [[:Категория:Задачи: Максимальный поток|Задачи: Максимальный поток]]&lt;br /&gt;
&lt;br /&gt;
[[Category: Максимальный поток]]&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=%D0%94%D0%BB%D0%B8%D0%BD%D0%BD%D0%B0%D1%8F_%D0%B0%D1%80%D0%B8%D1%84%D0%BC%D0%B5%D1%82%D0%B8%D0%BA%D0%B0&amp;diff=2931</id>
		<title>Длинная арифметика</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=%D0%94%D0%BB%D0%B8%D0%BD%D0%BD%D0%B0%D1%8F_%D0%B0%D1%80%D0%B8%D1%84%D0%BC%D0%B5%D1%82%D0%B8%D0%BA%D0%B0&amp;diff=2931"/>
		<updated>2025-10-04T14:22:52Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: /* Длинная арифметика на vector */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Хранение, ввод и вывод ==&lt;br /&gt;
&lt;br /&gt;
 class BigInteger {&lt;br /&gt;
     static const int BASE = 1e9;&lt;br /&gt;
     static const int BASE_LEN = 9;&lt;br /&gt;
     static const int MAX_SIZE = 1e4;&lt;br /&gt;
     int d[MAX_SIZE];&lt;br /&gt;
     int size;&lt;br /&gt;
 public:&lt;br /&gt;
     BigInteger(long long val = 0) {&lt;br /&gt;
         for (int i = 0; i &amp;lt; MAX_SIZE; i++)&lt;br /&gt;
             d[i] = 0;&lt;br /&gt;
         size = 0;&lt;br /&gt;
         if (val == 0)&lt;br /&gt;
             d[size++] = 0;&lt;br /&gt;
         while (val &amp;gt; 0) {&lt;br /&gt;
             d[size++] = val % BASE;&lt;br /&gt;
             val /= BASE;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
     BigInteger(char val[]) {&lt;br /&gt;
         for (int i = 0; i &amp;lt; MAX_SIZE; i++)&lt;br /&gt;
             d[i] = 0;&lt;br /&gt;
         size = 0;&lt;br /&gt;
         for (int i = strlen(val) - 1; i &amp;gt;= 0; i -= BASE_LEN) {&lt;br /&gt;
             int digit = 0;&lt;br /&gt;
             for (int j = max(0, i - BASE_LEN + 1); j &amp;lt;= i; j++)&lt;br /&gt;
                 digit = digit * 10 + val[j] - &#039;0&#039;;&lt;br /&gt;
             d[size++] = digit;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
     void print() const {&lt;br /&gt;
         static char spec[] = &amp;quot;%00d&amp;quot;;&lt;br /&gt;
         spec[2] = BASE_LEN + &#039;0&#039;;&lt;br /&gt;
         printf(&amp;quot;%d&amp;quot;, d[size - 1]);&lt;br /&gt;
         for (int i = size - 2; i &amp;gt;= 0; i--)&lt;br /&gt;
             printf(spec, d[i]);&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
== Сравнение ==&lt;br /&gt;
&lt;br /&gt;
 int cmp(const BigInteger &amp;amp;that) const {&lt;br /&gt;
     if (size != that.size)&lt;br /&gt;
         return size &amp;lt; that.size ? -1 : 1;&lt;br /&gt;
     for (int i = size - 1; i &amp;gt;= 0; i--) {&lt;br /&gt;
         if (d[i] != that.d[i])&lt;br /&gt;
             return d[i] &amp;lt; that.d[i] ? -1 : 1;&lt;br /&gt;
     }&lt;br /&gt;
     return 0;&lt;br /&gt;
 } &lt;br /&gt;
 bool operator &amp;lt; (const BigInteger &amp;amp;that) const {&lt;br /&gt;
     return cmp(that) &amp;lt; 0;&lt;br /&gt;
 }&lt;br /&gt;
 bool operator == (const BigInteger &amp;amp;that) const {&lt;br /&gt;
     return cmp(that) == 0;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Сложение ==&lt;br /&gt;
&lt;br /&gt;
 BigInteger operator + (const BigInteger &amp;amp;that) const {&lt;br /&gt;
     BigInteger res;        &lt;br /&gt;
     int carry = 0;&lt;br /&gt;
     for (int i = 0; i &amp;lt; max(size, that.size) || carry != 0; i++) {&lt;br /&gt;
         long long x = 0LL + d[i] + that.d[i] + carry;&lt;br /&gt;
         res.d[i] = x % BASE;&lt;br /&gt;
         carry = x / BASE;&lt;br /&gt;
     }&lt;br /&gt;
     res.size = max(size, that.size) + 1;&lt;br /&gt;
     while (res.size &amp;gt; 1 &amp;amp;&amp;amp; res.d[res.size - 1] == 0)&lt;br /&gt;
         res.size--;&lt;br /&gt;
     return res;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Вычитание ==&lt;br /&gt;
&lt;br /&gt;
Предполагается, что уменьшаемое больше вычитаемого.&lt;br /&gt;
&lt;br /&gt;
 BigInteger operator - (const BigInteger &amp;amp;that) const {&lt;br /&gt;
     BigInteger res;&lt;br /&gt;
     int carry = 0;&lt;br /&gt;
     for (int i = 0; i &amp;lt; size || carry != 0; i++) {&lt;br /&gt;
         long long x = 0LL + d[i] - that.d[i] + carry;&lt;br /&gt;
         res.d[i] = (x + BASE) % BASE;&lt;br /&gt;
         carry = x &amp;lt; 0 ? -1 : 0;&lt;br /&gt;
     }&lt;br /&gt;
     res.size = size;&lt;br /&gt;
     while (res.size &amp;gt; 1 &amp;amp;&amp;amp; res.d[res.size - 1] == 0)&lt;br /&gt;
         res.size--;&lt;br /&gt;
     return res;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Умножение ==&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
| width=&amp;quot;50%&amp;quot;  |&lt;br /&gt;
 BigInteger operator * (const BigInteger &amp;amp;that) const {&lt;br /&gt;
     BigInteger res;        &lt;br /&gt;
     for (int i = 0; i &amp;lt; size; i++) {&lt;br /&gt;
         int carry = 0;&lt;br /&gt;
         for (int j = 0; j &amp;lt; that.size || carry != 0; j++) {&lt;br /&gt;
             long long x = res.d[i + j] + 1LL * d[i] * that.d[j] + carry;&lt;br /&gt;
             res.d[i + j] = x % BASE;&lt;br /&gt;
             carry = x / BASE;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
     res.size = size + that.size;&lt;br /&gt;
     while (res.size &amp;gt; 1 &amp;amp;&amp;amp; res.d[res.size - 1] == 0)&lt;br /&gt;
         res.size--;&lt;br /&gt;
     return res;&lt;br /&gt;
 }&lt;br /&gt;
| width=&amp;quot;10px&amp;quot; |&lt;br /&gt;
| width=&amp;quot;50%&amp;quot;  |&lt;br /&gt;
 BigInteger operator * (int that) const {&lt;br /&gt;
     BigInteger res;        &lt;br /&gt;
     int carry = 0;&lt;br /&gt;
     for (int i = 0; i &amp;lt; size || carry != 0; i++) {&lt;br /&gt;
         long long x = 1LL * d[i] * that + carry;&lt;br /&gt;
         res.d[i] = x % BASE;&lt;br /&gt;
         carry = x / BASE;&lt;br /&gt;
     }&lt;br /&gt;
     res.size = size + 1;&lt;br /&gt;
     while (res.size &amp;gt; 1 &amp;amp;&amp;amp; res.d[res.size - 1] == 0)&lt;br /&gt;
         res.size--;&lt;br /&gt;
     return res;&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;
| width=&amp;quot;50%&amp;quot;  |&lt;br /&gt;
 BigInteger operator / (const BigInteger &amp;amp;that) const {&lt;br /&gt;
     BigInteger res, carry;&lt;br /&gt;
     for (int i = size - 1; i &amp;gt;= 0; i--) {&lt;br /&gt;
         carry = carry * BASE;&lt;br /&gt;
         carry.d[0] = d[i];&lt;br /&gt;
         int l = 0, r = BASE - 1, m;&lt;br /&gt;
         while (l + 1 &amp;lt; r) {&lt;br /&gt;
             m = l + (r - l) / 2;&lt;br /&gt;
             if ((that * m).cmp(carry) &amp;lt;= 0)&lt;br /&gt;
                 l = m;&lt;br /&gt;
             else&lt;br /&gt;
                 r = m;&lt;br /&gt;
         }&lt;br /&gt;
         res.d[i] = (that * r).cmp(carry) &amp;lt;= 0 ? r : l;&lt;br /&gt;
         carry = carry - that * res.d[i];&lt;br /&gt;
     }    &lt;br /&gt;
     res.size = size;&lt;br /&gt;
     while (res.size &amp;gt; 1 &amp;amp;&amp;amp; res.d[res.size - 1] == 0)&lt;br /&gt;
         res.size--;&lt;br /&gt;
     return res;&lt;br /&gt;
 }&lt;br /&gt;
| width=&amp;quot;10px&amp;quot; |&lt;br /&gt;
| width=&amp;quot;50%&amp;quot;  |&lt;br /&gt;
 BigInteger operator / (int that) const {&lt;br /&gt;
     BigInteger res;&lt;br /&gt;
     int carry = 0;&lt;br /&gt;
     for (int i = size - 1; i &amp;gt;= 0; i--) {&lt;br /&gt;
         long long x = 1LL * carry * BASE + d[i];&lt;br /&gt;
         res.d[i] = x / that;&lt;br /&gt;
         carry = x % that;&lt;br /&gt;
     }&lt;br /&gt;
     res.size = size;&lt;br /&gt;
     while (res.size &amp;gt; 1 &amp;amp;&amp;amp; res.d[res.size - 1] == 0)&lt;br /&gt;
         res.size--;&lt;br /&gt;
     return res;&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;
| width=&amp;quot;50%&amp;quot;  |&lt;br /&gt;
 BigInteger operator % (const BigInteger &amp;amp;that) const {&lt;br /&gt;
     BigInteger carry;&lt;br /&gt;
     for (int i = size - 1; i &amp;gt;= 0; i--) {&lt;br /&gt;
         carry = carry * BASE;&lt;br /&gt;
         carry.d[0] = d[i];&lt;br /&gt;
         int l = 0, r = BASE - 1, m;&lt;br /&gt;
         while (l + 1 &amp;lt; r) {&lt;br /&gt;
             m = l + (r - l) / 2;&lt;br /&gt;
             if ((that * m).cmp(carry) &amp;lt;= 0)&lt;br /&gt;
                 l = m;&lt;br /&gt;
             else&lt;br /&gt;
                 r = m;&lt;br /&gt;
         }&lt;br /&gt;
         int digit = (that * r).cmp(carry) &amp;lt;= 0 ? r : l;&lt;br /&gt;
         carry = carry - that * digit;&lt;br /&gt;
     }&lt;br /&gt;
     return carry;&lt;br /&gt;
 }&lt;br /&gt;
| width=&amp;quot;10px&amp;quot; |&lt;br /&gt;
| width=&amp;quot;50%&amp;quot;  |&lt;br /&gt;
 int operator % (int that) const {&lt;br /&gt;
     int carry = 0;&lt;br /&gt;
     for (int i = size - 1; i &amp;gt;= 0; i--)&lt;br /&gt;
         carry = (1LL * carry * BASE + d[i]) % that;&lt;br /&gt;
     return carry;&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;
|}&lt;br /&gt;
&lt;br /&gt;
== Длинная арифметика на vector ==&lt;br /&gt;
&lt;br /&gt;
 struct BigInteger {&lt;br /&gt;
     vector&amp;lt;int&amp;gt; digits;&lt;br /&gt;
     inline static const int BASE = 1e9;&lt;br /&gt;
     inline static const int DIGIT_WIDTH = 9;&lt;br /&gt;
 &lt;br /&gt;
     inline int digit(int index) const {&lt;br /&gt;
         return index &amp;lt; digits.size() ? digits[index] : 0;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     inline int &amp;amp;digit(int index) {&lt;br /&gt;
         if (digits.size() &amp;lt;= index)&lt;br /&gt;
             digits.resize(index + 1);&lt;br /&gt;
         return digits[index];&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     inline void removeZeros() {&lt;br /&gt;
         while (!digits.empty() &amp;amp;&amp;amp; !digits.back())&lt;br /&gt;
             digits.pop_back();&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     BigInteger(long long value = 0) {&lt;br /&gt;
         for (; value; value /= BASE)&lt;br /&gt;
             digits.push_back(value % BASE);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     BigInteger(const string &amp;amp;s) {&lt;br /&gt;
         for (int r = s.size() - 1; r &amp;gt;= 0; r -= DIGIT_WIDTH) {&lt;br /&gt;
             int l = max(r - DIGIT_WIDTH + 1, 0);&lt;br /&gt;
             digits.push_back(stoi(s.substr(l, r - l + 1)));&lt;br /&gt;
         }&lt;br /&gt;
         removeZeros();&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     bool operator &amp;lt; (const BigInteger &amp;amp;that) const {&lt;br /&gt;
         if (digits.size() != that.digits.size())&lt;br /&gt;
             return digits.size() &amp;lt; that.digits.size();&lt;br /&gt;
         for (int i = digits.size() - 1; i &amp;gt;= 0; i--)&lt;br /&gt;
             if (digit(i) != that.digit(i))&lt;br /&gt;
                 return digit(i) &amp;lt; that.digit(i);&lt;br /&gt;
         return 0;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     BigInteger operator + (const BigInteger &amp;amp;that) const {&lt;br /&gt;
         BigInteger res;&lt;br /&gt;
         for (int i = 0, carry = 0; i &amp;lt; digits.size() || i &amp;lt; that.digits.size() || carry; i++) {&lt;br /&gt;
             int cur = digit(i) + that.digit(i) + carry;&lt;br /&gt;
             res.digits.push_back(cur % BASE);&lt;br /&gt;
             carry = cur / BASE;&lt;br /&gt;
         }&lt;br /&gt;
         return res;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     BigInteger operator - (const BigInteger &amp;amp;that) const {&lt;br /&gt;
         BigInteger res;&lt;br /&gt;
         for (int i = 0, carry = 0; i &amp;lt; digits.size() || carry; i++) {&lt;br /&gt;
             int cur = digit(i) - that.digit(i) + carry;&lt;br /&gt;
             res.digits.push_back((cur + BASE) % BASE);&lt;br /&gt;
             carry = cur &amp;lt; 0 ? -1 : 0;&lt;br /&gt;
         }&lt;br /&gt;
         res.removeZeros();&lt;br /&gt;
         return res;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     BigInteger operator * (const BigInteger &amp;amp;that) const {&lt;br /&gt;
         BigInteger res;&lt;br /&gt;
         for (int i = 0, carry = 0; i &amp;lt; digits.size(); i++) {&lt;br /&gt;
             for (int j = 0; j &amp;lt; that.digits.size() || carry; j++) {&lt;br /&gt;
                 long long cur = res.digit(i + j) + 1LL * digit(i) * that.digit(j) + carry;&lt;br /&gt;
                 res.digit(i + j) = cur % BASE;&lt;br /&gt;
                 carry = cur / BASE;&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         res.removeZeros();&lt;br /&gt;
         return res;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     BigInteger operator / (int that) const {&lt;br /&gt;
         BigInteger res;&lt;br /&gt;
         for (int i = digits.size() - 1, carry = 0; i &amp;gt;= 0; i--) {&lt;br /&gt;
             long long cur = 1LL * carry * BASE + digit(i);&lt;br /&gt;
             res.digit(i) = cur / that;&lt;br /&gt;
             carry = cur % that;&lt;br /&gt;
         }&lt;br /&gt;
         res.removeZeros();&lt;br /&gt;
         return res;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     int operator % (int that) const {&lt;br /&gt;
         long long res = 0;&lt;br /&gt;
         for (int i = digits.size() - 1; i &amp;gt;= 0; i--)&lt;br /&gt;
             res = (res * BASE + digit(i)) % that;&lt;br /&gt;
         return res;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     friend istream &amp;amp;operator &amp;gt;&amp;gt; (istream &amp;amp;in, BigInteger &amp;amp;value) {&lt;br /&gt;
         string s;&lt;br /&gt;
         in &amp;gt;&amp;gt; s;&lt;br /&gt;
         value = BigInteger(s);&lt;br /&gt;
         return in;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     friend ostream &amp;amp;operator &amp;lt;&amp;lt; (ostream &amp;amp;out, const BigInteger &amp;amp;value) {&lt;br /&gt;
         if (value.digits.empty()) {&lt;br /&gt;
             out &amp;lt;&amp;lt; 0;&lt;br /&gt;
         } else {&lt;br /&gt;
             out &amp;lt;&amp;lt; value.digits.back();&lt;br /&gt;
             for (int i = (int)value.digits.size() - 2; i &amp;gt;= 0; i--) {&lt;br /&gt;
                 out.width(DIGIT_WIDTH);&lt;br /&gt;
                 out.fill(&#039;0&#039;);&lt;br /&gt;
                 out &amp;lt;&amp;lt; value.digits[i];&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         return out;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
== Ссылки на задачи ==&lt;br /&gt;
* [http://acmp.ru/?main=task&amp;amp;id_task=103 ACMP #103 &amp;amp;mdash; Снова A + B]&lt;br /&gt;
* [http://acmp.ru/?main=task&amp;amp;id_task=143 ACMP #143 &amp;amp;mdash; A - B]&lt;br /&gt;
* [http://acmp.ru/?main=task&amp;amp;id_task=144 ACMP #144 &amp;amp;mdash; A * B]&lt;br /&gt;
* [http://acmp.ru/?main=task&amp;amp;id_task=145 ACMP #145 &amp;amp;mdash; A div B]&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
* [[Сложная длинная арифметика]]&lt;br /&gt;
* [http://e-maxx.ru/algo/big_integer e-maxx.ru &amp;amp;mdash; Длинная арифметика]&lt;br /&gt;
* [http://cppalgo.blogspot.ru/2010/05/blog-post.html cppalgo.blogspot.ru &amp;amp;mdash; Длинная арифметика]&lt;br /&gt;
* [http://brestprog.neocities.org/lections/longarithmetics.html brestprog.neocities.org &amp;amp;mdash; Длинная арифметика]&lt;br /&gt;
* [http://informatics.mccme.ru/course/view.php?id=17 informatics.mccme.ru &amp;amp;mdash; Курс &amp;amp;laquo;Арифметика и числовые алгоритмы&amp;amp;raquo; &amp;amp;mdash; часть 5]&lt;br /&gt;
* [http://github.com/ADJA/algos/blob/master/NumberTheory/BigInt.cpp Algos &amp;amp;mdash; Structure implementing long arithmetic in C++]&lt;br /&gt;
&lt;br /&gt;
[[Category:Арифметические алгоритмы]]&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%94%D0%B8%D0%BD%D0%B8%D1%86%D0%B0&amp;diff=2930</id>
		<title>Алгоритм Диница</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%94%D0%B8%D0%BD%D0%B8%D1%86%D0%B0&amp;diff=2930"/>
		<updated>2025-08-07T22:23:27Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: /* Ссылки */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt; class Graph {&lt;br /&gt;
     struct Edge {&lt;br /&gt;
         int a, b, capacity, flow = 0;&lt;br /&gt;
 &lt;br /&gt;
         Edge(int a, int b, int capacity) :&lt;br /&gt;
             a(a), b(b), capacity(capacity) {}&lt;br /&gt;
 &lt;br /&gt;
         int other(int v) const {&lt;br /&gt;
             return v == a ? b : a;&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         int capacityTo(int v) const {&lt;br /&gt;
             return v == b ? capacity - flow : flow;&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         void addFlowTo(int v, int deltaFlow) {&lt;br /&gt;
             flow += (v == b ? deltaFlow : -deltaFlow);&lt;br /&gt;
         }&lt;br /&gt;
     };&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;Edge&amp;gt; edges;&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; graph;&lt;br /&gt;
     vector&amp;lt;int&amp;gt; dist, edgeTo, index;&lt;br /&gt;
 &lt;br /&gt;
     bool bfs(int start, int finish) {&lt;br /&gt;
         dist.assign(graph.size(), 1e9);&lt;br /&gt;
         queue&amp;lt;int&amp;gt; q;&lt;br /&gt;
 &lt;br /&gt;
         dist[start] = 0;&lt;br /&gt;
         q.push(start);&lt;br /&gt;
 &lt;br /&gt;
         while (!q.empty()) {&lt;br /&gt;
             int v = q.front();&lt;br /&gt;
             q.pop();&lt;br /&gt;
 &lt;br /&gt;
             for (int e : graph[v]) {&lt;br /&gt;
                 int to = edges[e].other(v);&lt;br /&gt;
                 if (edges[e].capacityTo(to) &amp;amp;&amp;amp; dist[to] &amp;gt; dist[v] + 1) {&lt;br /&gt;
                     dist[to] = dist[v] + 1;&lt;br /&gt;
                     q.push(to);&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         return dist[finish] != 1e9;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     bool dfs(int v, int finish) {&lt;br /&gt;
         if (v == finish)&lt;br /&gt;
             return 1;&lt;br /&gt;
 &lt;br /&gt;
         for ( ; index[v] &amp;lt; graph[v].size(); index[v]++) {&lt;br /&gt;
             int e = graph[v][index[v]], to = edges[e].other(v);&lt;br /&gt;
             if (edges[e].capacityTo(to) &amp;amp;&amp;amp; dist[to] == dist[v] + 1 &amp;amp;&amp;amp; dfs(to, finish)) {&lt;br /&gt;
                 edgeTo[to] = e;&lt;br /&gt;
                 return 1;&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         return 0;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     int bottleneckCapacity(int start, int finish) {&lt;br /&gt;
         int bCapacity = 1e9;&lt;br /&gt;
         for (int v = finish; v != start; v = edges[edgeTo[v]].other(v))&lt;br /&gt;
             bCapacity = min(bCapacity, edges[edgeTo[v]].capacityTo(v));&lt;br /&gt;
         return bCapacity;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     void addFlow(int start, int finish, int deltaFlow) {&lt;br /&gt;
         for (int v = finish; v != start; v = edges[edgeTo[v]].other(v))&lt;br /&gt;
             edges[edgeTo[v]].addFlowTo(v, deltaFlow);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
 public:&lt;br /&gt;
     Graph(int vertexCount) :&lt;br /&gt;
         graph(vertexCount), dist(vertexCount), edgeTo(vertexCount), index(vertexCount) {}&lt;br /&gt;
 &lt;br /&gt;
     void addEdge(int from, int to, int capacity) {&lt;br /&gt;
         edges.push_back(Edge(from, to, capacity));&lt;br /&gt;
         graph[from].push_back(edges.size() - 1);&lt;br /&gt;
         graph[to].push_back(edges.size() - 1);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     long long maxFlow(int start, int finish) {&lt;br /&gt;
         long long flow = 0;&lt;br /&gt;
         while (bfs(start, finish)) {&lt;br /&gt;
             index.assign(graph.size(), 0);&lt;br /&gt;
             while (dfs(start, finish)) {&lt;br /&gt;
                 int deltaFlow = bottleneckCapacity(start, finish);&lt;br /&gt;
                 addFlow(start, finish, deltaFlow);&lt;br /&gt;
                 flow += deltaFlow;&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         return flow;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
Теория:&lt;br /&gt;
* [http://e-maxx.ru/algo/dinic e-maxx.ru — Алгоритм Диница нахождения максимального потока]&lt;br /&gt;
* [https://cp-algorithms.com/graph/dinic.html cp-algorithms.com — Maximum flow — Dinic&#039;s algorithm]&lt;br /&gt;
* [http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D1%85%D0%B5%D0%BC%D0%B0_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0_%D0%94%D0%B8%D0%BD%D0%B8%D1%86%D0%B0 neerc.ifmo.ru/wiki — Схема алгоритма Диница]&lt;br /&gt;
* [https://wiki.algocode.ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%94%D0%B8%D0%BD%D0%B8%D1%86%D0%B0 wiki.algocode.ru — Алгоритм Диница]&lt;br /&gt;
* [https://codeforces.com/blog/entry/104960 codeforces.com — My way of understanding Dinic&#039;s algorithm]&lt;br /&gt;
* [https://codeforces.com/blog/entry/145343 codeforces.com — Worst-Case Graphs for Maximum Flow Algorithms]&lt;br /&gt;
* [https://usaco.guide/adv/max-flow?lang=cpp#dinics-algorithm usaco.guide — Dinic&#039;s Algorithm]&lt;br /&gt;
Демонстрация:&lt;br /&gt;
* [https://visualgo.net/en/maxflow visualgo.net — Network Flow]&lt;br /&gt;
Код:&lt;br /&gt;
* [https://github.com/indy256/codelibrary/blob/master/cpp/graphs/flows/max_flow_dinic.h indy256/codelibrary/cpp/graphs/flows/max_flow_dinic.h]&lt;br /&gt;
* [https://github.com/ADJA/algos/blob/master/Graphs/Dinic.cpp ADJA/algos/Graphs/Dinic.cpp]&lt;br /&gt;
Задачи:&lt;br /&gt;
* [http://informatics.mccme.ru/course/view.php?id=6 informatics.mccme.ru &amp;amp;mdash; Курс &amp;amp;laquo;Алгоритмы на графах&amp;amp;raquo; &amp;amp;mdash; часть 9]&lt;br /&gt;
&lt;br /&gt;
[[Category: Максимальный поток]]&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%AD%D0%B4%D0%BC%D0%BE%D0%BD%D0%B4%D1%81%D0%B0-%D0%9A%D0%B0%D1%80%D0%BF%D0%B0&amp;diff=2929</id>
		<title>Алгоритм Эдмондса-Карпа</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%AD%D0%B4%D0%BC%D0%BE%D0%BD%D0%B4%D1%81%D0%B0-%D0%9A%D0%B0%D1%80%D0%BF%D0%B0&amp;diff=2929"/>
		<updated>2025-08-07T22:23:13Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: /* Ссылки */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt; class Graph {&lt;br /&gt;
     struct Edge {&lt;br /&gt;
         int a, b, capacity, flow = 0;&lt;br /&gt;
 &lt;br /&gt;
         Edge(int a, int b, int capacity) :&lt;br /&gt;
             a(a), b(b), capacity(capacity) {}&lt;br /&gt;
 &lt;br /&gt;
         int other(int v) const {&lt;br /&gt;
             return v == a ? b : a;&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         int capacityTo(int v) const {&lt;br /&gt;
             return v == b ? capacity - flow : flow;&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         void addFlowTo(int v, int deltaFlow) {&lt;br /&gt;
             flow += (v == b ? deltaFlow : -deltaFlow);&lt;br /&gt;
         }&lt;br /&gt;
     };&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;Edge&amp;gt; edges;&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; graph;&lt;br /&gt;
     vector&amp;lt;bool&amp;gt; visited;&lt;br /&gt;
     vector&amp;lt;int&amp;gt; edgeTo;&lt;br /&gt;
 &lt;br /&gt;
     void bfs(int start) {&lt;br /&gt;
         queue&amp;lt;int&amp;gt; q;&lt;br /&gt;
         visited[start] = 1;&lt;br /&gt;
         q.push(start);&lt;br /&gt;
         while (!q.empty()) {&lt;br /&gt;
             int v = q.front();&lt;br /&gt;
             q.pop();&lt;br /&gt;
             for (int e : graph[v]) {&lt;br /&gt;
                 int to = edges[e].other(v);&lt;br /&gt;
                 if (!visited[to] &amp;amp;&amp;amp; edges[e].capacityTo(to)) {&lt;br /&gt;
                     edgeTo[to] = e;&lt;br /&gt;
                     visited[to] = 1;&lt;br /&gt;
                     q.push(to);&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     bool hasPath(int start, int finish) {&lt;br /&gt;
         visited.assign(visited.size(), 0);&lt;br /&gt;
         bfs(start);&lt;br /&gt;
         return visited[finish];&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     int bottleneckCapacity(int start, int finish) {&lt;br /&gt;
         int bCapacity = 1e9;&lt;br /&gt;
         for (int v = finish; v != start; v = edges[edgeTo[v]].other(v))&lt;br /&gt;
             bCapacity = min(bCapacity, edges[edgeTo[v]].capacityTo(v));&lt;br /&gt;
         return bCapacity;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     void addFlow(int start, int finish, int deltaFlow) {&lt;br /&gt;
         for (int v = finish; v != start; v = edges[edgeTo[v]].other(v))&lt;br /&gt;
             edges[edgeTo[v]].addFlowTo(v, deltaFlow);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
 public:&lt;br /&gt;
     Graph(int vertexCount) :&lt;br /&gt;
         graph(vertexCount), visited(vertexCount), edgeTo(vertexCount) {}&lt;br /&gt;
 &lt;br /&gt;
     void addEdge(int from, int to, int capacity) {&lt;br /&gt;
         edges.push_back(Edge(from, to, capacity));&lt;br /&gt;
         graph[from].push_back(edges.size() - 1);&lt;br /&gt;
         graph[to].push_back(edges.size() - 1);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     long long maxFlow(int start, int finish) {&lt;br /&gt;
         long long flow = 0;&lt;br /&gt;
         while (hasPath(start, finish)) {&lt;br /&gt;
             int deltaFlow = bottleneckCapacity(start, finish);&lt;br /&gt;
             addFlow(start, finish, deltaFlow);&lt;br /&gt;
             flow += deltaFlow;&lt;br /&gt;
         }&lt;br /&gt;
         return flow;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
Теория:&lt;br /&gt;
* [https://algs4.cs.princeton.edu/lectures/keynote/64MaxFlow.pdf algs4.cs.princeton.edu — 6.4 Maximum Flow]&lt;br /&gt;
* [http://e-maxx.ru/algo/edmonds_karp e-maxx.ru — Алгоритм Эдмондса-Карпа нахождения максимального потока за O (NM^2)]&lt;br /&gt;
* [http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%AD%D0%B4%D0%BC%D0%BE%D0%BD%D0%B4%D1%81%D0%B0-%D0%9A%D0%B0%D1%80%D0%BF%D0%B0 neerc.ifmo.ru/wiki — Алгоритм Эдмондса-Карпа]&lt;br /&gt;
* [http://brilliant.org/wiki/edmonds-karp-algorithm Brilliant.org — Edmonds-Karp Algorithm]&lt;br /&gt;
* [https://codeforces.com/blog/entry/145343 codeforces.com — Worst-Case Graphs for Maximum Flow Algorithms]&lt;br /&gt;
Демонстрация:&lt;br /&gt;
* [https://visualgo.net/en/maxflow VisuAlgo — Network Flow]&lt;br /&gt;
Код:&lt;br /&gt;
* [https://github.com/indy256/codelibrary/blob/master/java/graphs/flows/MaxFlowEdmondsKarp.java CodeLibrary &amp;amp;mdash; Maximum flow. Edmonds-Karp algorithm in O(min(E^2 * V, E * FLOW))]&lt;br /&gt;
* algs4.cs.princeton.edu/code &amp;amp;mdash; [http://algs4.cs.princeton.edu/code/edu/princeton/cs/algs4/FlowEdge.java.html capacitated edge with flow], [http://algs4.cs.princeton.edu/code/edu/princeton/cs/algs4/FlowNetwork.java.html capacitated network], [http://algs4.cs.princeton.edu/code/edu/princeton/cs/algs4/FordFulkerson.java.html maxflow–mincut] (несмотря на название, используется алгоритм Эдмондса-Карпа)&lt;br /&gt;
Задачи:&lt;br /&gt;
* [http://informatics.mccme.ru/course/view.php?id=6 informatics.mccme.ru &amp;amp;mdash; Курс &amp;amp;laquo;Алгоритмы на графах&amp;amp;raquo; &amp;amp;mdash; часть 9]&lt;br /&gt;
* [[:Категория:Задачи: Максимальный поток|Задачи: Максимальный поток]]&lt;br /&gt;
&lt;br /&gt;
[[Category: Максимальный поток]]&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A4%D0%BE%D1%80%D0%B4%D0%B0-%D0%A4%D0%B0%D0%BB%D0%BA%D0%B5%D1%80%D1%81%D0%BE%D0%BD%D0%B0&amp;diff=2928</id>
		<title>Алгоритм Форда-Фалкерсона</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A4%D0%BE%D1%80%D0%B4%D0%B0-%D0%A4%D0%B0%D0%BB%D0%BA%D0%B5%D1%80%D1%81%D0%BE%D0%BD%D0%B0&amp;diff=2928"/>
		<updated>2025-08-07T22:22:53Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: /* Ссылки */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt; class Graph {&lt;br /&gt;
     struct Edge {&lt;br /&gt;
         int a, b, capacity, flow = 0;&lt;br /&gt;
 &lt;br /&gt;
         Edge(int a, int b, int capacity) :&lt;br /&gt;
             a(a), b(b), capacity(capacity) {}&lt;br /&gt;
 &lt;br /&gt;
         int other(int v) const {&lt;br /&gt;
             return v == a ? b : a;&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         int capacityTo(int v) const {&lt;br /&gt;
             return v == b ? capacity - flow : flow;&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         void addFlowTo(int v, int deltaFlow) {&lt;br /&gt;
             flow += (v == b ? deltaFlow : -deltaFlow);&lt;br /&gt;
         }&lt;br /&gt;
     };&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;Edge&amp;gt; edges;&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; graph;&lt;br /&gt;
     vector&amp;lt;bool&amp;gt; visited;&lt;br /&gt;
     vector&amp;lt;int&amp;gt; edgeTo;&lt;br /&gt;
 &lt;br /&gt;
     void dfs(int v) {&lt;br /&gt;
         visited[v] = 1;&lt;br /&gt;
         for (int e : graph[v]) {&lt;br /&gt;
             int to = edges[e].other(v);&lt;br /&gt;
             if (!visited[to] &amp;amp;&amp;amp; edges[e].capacityTo(to)) {&lt;br /&gt;
                 edgeTo[to] = e;&lt;br /&gt;
                 dfs(to);&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     bool hasPath(int start, int finish) {&lt;br /&gt;
         visited.assign(visited.size(), 0);&lt;br /&gt;
         dfs(start);&lt;br /&gt;
         return visited[finish];&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     int bottleneckCapacity(int start, int finish) {&lt;br /&gt;
         int bCapacity = 1e9;&lt;br /&gt;
         for (int v = finish; v != start; v = edges[edgeTo[v]].other(v))&lt;br /&gt;
             bCapacity = min(bCapacity, edges[edgeTo[v]].capacityTo(v));&lt;br /&gt;
         return bCapacity;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     void addFlow(int start, int finish, int deltaFlow) {&lt;br /&gt;
         for (int v = finish; v != start; v = edges[edgeTo[v]].other(v))&lt;br /&gt;
             edges[edgeTo[v]].addFlowTo(v, deltaFlow);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
 public:&lt;br /&gt;
     Graph(int vertexCount) :&lt;br /&gt;
         graph(vertexCount), visited(vertexCount), edgeTo(vertexCount) {}&lt;br /&gt;
         &lt;br /&gt;
     void addEdge(int from, int to, int capacity) {&lt;br /&gt;
         edges.push_back(Edge(from, to, capacity));&lt;br /&gt;
         graph[from].push_back(edges.size() - 1);&lt;br /&gt;
         graph[to].push_back(edges.size() - 1);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     long long maxFlow(int start, int finish) {&lt;br /&gt;
         long long flow = 0;&lt;br /&gt;
         while (hasPath(start, finish)) {&lt;br /&gt;
             int deltaFlow = bottleneckCapacity(start, finish);&lt;br /&gt;
             addFlow(start, finish, deltaFlow);&lt;br /&gt;
             flow += deltaFlow;&lt;br /&gt;
         }&lt;br /&gt;
         return flow;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
Теория:&lt;br /&gt;
* [https://algs4.cs.princeton.edu/lectures/keynote/64MaxFlow.pdf algs4.cs.princeton.edu — 6.4 Maximum Flow]&lt;br /&gt;
* [http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A4%D0%BE%D1%80%D0%B4%D0%B0-%D0%A4%D0%B0%D0%BB%D0%BA%D0%B5%D1%80%D1%81%D0%BE%D0%BD%D0%B0,_%D1%80%D0%B5%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F_%D1%81_%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D1%8C%D1%8E_%D0%BF%D0%BE%D0%B8%D1%81%D0%BA%D0%B0_%D0%B2_%D0%B3%D0%BB%D1%83%D0%B1%D0%B8%D0%BD%D1%83 neerc.ifmo.ru/wiki — Алгоритм Форда-Фалкерсона]&lt;br /&gt;
* [http://brilliant.org/wiki/ford-fulkerson-algorithm Brilliant.org — Ford-Fulkerson Algorithm]&lt;br /&gt;
* [https://codeforces.com/blog/entry/145343 codeforces.com — Worst-Case Graphs for Maximum Flow Algorithms]&lt;br /&gt;
Демонстрация:&lt;br /&gt;
* [https://visualgo.net/en/maxflow VisuAlgo — Network Flow]&lt;br /&gt;
Код:&lt;br /&gt;
* [https://github.com/indy256/codelibrary/blob/master/java/graphs/flows/MaxFlowFordFulkerson.java CodeLibrary — Maximum flow. Ford-Fulkerson alogithm in O(V^2 * FLOW)]&lt;br /&gt;
* [http://github.com/ADJA/algos/blob/master/Graphs/FordFulkerson.cpp Algos &amp;amp;mdash; Ford-Fulkerson maxflow]&lt;br /&gt;
* algs4.cs.princeton.edu/code &amp;amp;mdash; [http://algs4.cs.princeton.edu/code/edu/princeton/cs/algs4/FlowEdge.java.html capacitated edge with flow], [http://algs4.cs.princeton.edu/code/edu/princeton/cs/algs4/FlowNetwork.java.html capacitated network], [http://algs4.cs.princeton.edu/code/edu/princeton/cs/algs4/FordFulkerson.java.html maxflow–mincut] (несмотря на название, используется алгоритм Эдмондса-Карпа)&lt;br /&gt;
Задачи:&lt;br /&gt;
* [http://informatics.mccme.ru/course/view.php?id=6 informatics.mccme.ru &amp;amp;mdash; Курс &amp;amp;laquo;Алгоритмы на графах&amp;amp;raquo; &amp;amp;mdash; часть 9]&lt;br /&gt;
* [[:Категория:Задачи: Максимальный поток|Задачи: Максимальный поток]]&lt;br /&gt;
&lt;br /&gt;
[[Category: Максимальный поток]]&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=%D0%94%D0%BB%D0%B8%D0%BD%D0%BD%D0%B0%D1%8F_%D0%B0%D1%80%D0%B8%D1%84%D0%BC%D0%B5%D1%82%D0%B8%D0%BA%D0%B0&amp;diff=2927</id>
		<title>Длинная арифметика</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=%D0%94%D0%BB%D0%B8%D0%BD%D0%BD%D0%B0%D1%8F_%D0%B0%D1%80%D0%B8%D1%84%D0%BC%D0%B5%D1%82%D0%B8%D0%BA%D0%B0&amp;diff=2927"/>
		<updated>2025-04-13T19:01:11Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: /* Длинная арифметика на vector */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Хранение, ввод и вывод ==&lt;br /&gt;
&lt;br /&gt;
 class BigInteger {&lt;br /&gt;
     static const int BASE = 1e9;&lt;br /&gt;
     static const int BASE_LEN = 9;&lt;br /&gt;
     static const int MAX_SIZE = 1e4;&lt;br /&gt;
     int d[MAX_SIZE];&lt;br /&gt;
     int size;&lt;br /&gt;
 public:&lt;br /&gt;
     BigInteger(long long val = 0) {&lt;br /&gt;
         for (int i = 0; i &amp;lt; MAX_SIZE; i++)&lt;br /&gt;
             d[i] = 0;&lt;br /&gt;
         size = 0;&lt;br /&gt;
         if (val == 0)&lt;br /&gt;
             d[size++] = 0;&lt;br /&gt;
         while (val &amp;gt; 0) {&lt;br /&gt;
             d[size++] = val % BASE;&lt;br /&gt;
             val /= BASE;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
     BigInteger(char val[]) {&lt;br /&gt;
         for (int i = 0; i &amp;lt; MAX_SIZE; i++)&lt;br /&gt;
             d[i] = 0;&lt;br /&gt;
         size = 0;&lt;br /&gt;
         for (int i = strlen(val) - 1; i &amp;gt;= 0; i -= BASE_LEN) {&lt;br /&gt;
             int digit = 0;&lt;br /&gt;
             for (int j = max(0, i - BASE_LEN + 1); j &amp;lt;= i; j++)&lt;br /&gt;
                 digit = digit * 10 + val[j] - &#039;0&#039;;&lt;br /&gt;
             d[size++] = digit;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
     void print() const {&lt;br /&gt;
         static char spec[] = &amp;quot;%00d&amp;quot;;&lt;br /&gt;
         spec[2] = BASE_LEN + &#039;0&#039;;&lt;br /&gt;
         printf(&amp;quot;%d&amp;quot;, d[size - 1]);&lt;br /&gt;
         for (int i = size - 2; i &amp;gt;= 0; i--)&lt;br /&gt;
             printf(spec, d[i]);&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
== Сравнение ==&lt;br /&gt;
&lt;br /&gt;
 int cmp(const BigInteger &amp;amp;that) const {&lt;br /&gt;
     if (size != that.size)&lt;br /&gt;
         return size &amp;lt; that.size ? -1 : 1;&lt;br /&gt;
     for (int i = size - 1; i &amp;gt;= 0; i--) {&lt;br /&gt;
         if (d[i] != that.d[i])&lt;br /&gt;
             return d[i] &amp;lt; that.d[i] ? -1 : 1;&lt;br /&gt;
     }&lt;br /&gt;
     return 0;&lt;br /&gt;
 } &lt;br /&gt;
 bool operator &amp;lt; (const BigInteger &amp;amp;that) const {&lt;br /&gt;
     return cmp(that) &amp;lt; 0;&lt;br /&gt;
 }&lt;br /&gt;
 bool operator == (const BigInteger &amp;amp;that) const {&lt;br /&gt;
     return cmp(that) == 0;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Сложение ==&lt;br /&gt;
&lt;br /&gt;
 BigInteger operator + (const BigInteger &amp;amp;that) const {&lt;br /&gt;
     BigInteger res;        &lt;br /&gt;
     int carry = 0;&lt;br /&gt;
     for (int i = 0; i &amp;lt; max(size, that.size) || carry != 0; i++) {&lt;br /&gt;
         long long x = 0LL + d[i] + that.d[i] + carry;&lt;br /&gt;
         res.d[i] = x % BASE;&lt;br /&gt;
         carry = x / BASE;&lt;br /&gt;
     }&lt;br /&gt;
     res.size = max(size, that.size) + 1;&lt;br /&gt;
     while (res.size &amp;gt; 1 &amp;amp;&amp;amp; res.d[res.size - 1] == 0)&lt;br /&gt;
         res.size--;&lt;br /&gt;
     return res;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Вычитание ==&lt;br /&gt;
&lt;br /&gt;
Предполагается, что уменьшаемое больше вычитаемого.&lt;br /&gt;
&lt;br /&gt;
 BigInteger operator - (const BigInteger &amp;amp;that) const {&lt;br /&gt;
     BigInteger res;&lt;br /&gt;
     int carry = 0;&lt;br /&gt;
     for (int i = 0; i &amp;lt; size || carry != 0; i++) {&lt;br /&gt;
         long long x = 0LL + d[i] - that.d[i] + carry;&lt;br /&gt;
         res.d[i] = (x + BASE) % BASE;&lt;br /&gt;
         carry = x &amp;lt; 0 ? -1 : 0;&lt;br /&gt;
     }&lt;br /&gt;
     res.size = size;&lt;br /&gt;
     while (res.size &amp;gt; 1 &amp;amp;&amp;amp; res.d[res.size - 1] == 0)&lt;br /&gt;
         res.size--;&lt;br /&gt;
     return res;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Умножение ==&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
| width=&amp;quot;50%&amp;quot;  |&lt;br /&gt;
 BigInteger operator * (const BigInteger &amp;amp;that) const {&lt;br /&gt;
     BigInteger res;        &lt;br /&gt;
     for (int i = 0; i &amp;lt; size; i++) {&lt;br /&gt;
         int carry = 0;&lt;br /&gt;
         for (int j = 0; j &amp;lt; that.size || carry != 0; j++) {&lt;br /&gt;
             long long x = res.d[i + j] + 1LL * d[i] * that.d[j] + carry;&lt;br /&gt;
             res.d[i + j] = x % BASE;&lt;br /&gt;
             carry = x / BASE;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
     res.size = size + that.size;&lt;br /&gt;
     while (res.size &amp;gt; 1 &amp;amp;&amp;amp; res.d[res.size - 1] == 0)&lt;br /&gt;
         res.size--;&lt;br /&gt;
     return res;&lt;br /&gt;
 }&lt;br /&gt;
| width=&amp;quot;10px&amp;quot; |&lt;br /&gt;
| width=&amp;quot;50%&amp;quot;  |&lt;br /&gt;
 BigInteger operator * (int that) const {&lt;br /&gt;
     BigInteger res;        &lt;br /&gt;
     int carry = 0;&lt;br /&gt;
     for (int i = 0; i &amp;lt; size || carry != 0; i++) {&lt;br /&gt;
         long long x = 1LL * d[i] * that + carry;&lt;br /&gt;
         res.d[i] = x % BASE;&lt;br /&gt;
         carry = x / BASE;&lt;br /&gt;
     }&lt;br /&gt;
     res.size = size + 1;&lt;br /&gt;
     while (res.size &amp;gt; 1 &amp;amp;&amp;amp; res.d[res.size - 1] == 0)&lt;br /&gt;
         res.size--;&lt;br /&gt;
     return res;&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;
| width=&amp;quot;50%&amp;quot;  |&lt;br /&gt;
 BigInteger operator / (const BigInteger &amp;amp;that) const {&lt;br /&gt;
     BigInteger res, carry;&lt;br /&gt;
     for (int i = size - 1; i &amp;gt;= 0; i--) {&lt;br /&gt;
         carry = carry * BASE;&lt;br /&gt;
         carry.d[0] = d[i];&lt;br /&gt;
         int l = 0, r = BASE - 1, m;&lt;br /&gt;
         while (l + 1 &amp;lt; r) {&lt;br /&gt;
             m = l + (r - l) / 2;&lt;br /&gt;
             if ((that * m).cmp(carry) &amp;lt;= 0)&lt;br /&gt;
                 l = m;&lt;br /&gt;
             else&lt;br /&gt;
                 r = m;&lt;br /&gt;
         }&lt;br /&gt;
         res.d[i] = (that * r).cmp(carry) &amp;lt;= 0 ? r : l;&lt;br /&gt;
         carry = carry - that * res.d[i];&lt;br /&gt;
     }    &lt;br /&gt;
     res.size = size;&lt;br /&gt;
     while (res.size &amp;gt; 1 &amp;amp;&amp;amp; res.d[res.size - 1] == 0)&lt;br /&gt;
         res.size--;&lt;br /&gt;
     return res;&lt;br /&gt;
 }&lt;br /&gt;
| width=&amp;quot;10px&amp;quot; |&lt;br /&gt;
| width=&amp;quot;50%&amp;quot;  |&lt;br /&gt;
 BigInteger operator / (int that) const {&lt;br /&gt;
     BigInteger res;&lt;br /&gt;
     int carry = 0;&lt;br /&gt;
     for (int i = size - 1; i &amp;gt;= 0; i--) {&lt;br /&gt;
         long long x = 1LL * carry * BASE + d[i];&lt;br /&gt;
         res.d[i] = x / that;&lt;br /&gt;
         carry = x % that;&lt;br /&gt;
     }&lt;br /&gt;
     res.size = size;&lt;br /&gt;
     while (res.size &amp;gt; 1 &amp;amp;&amp;amp; res.d[res.size - 1] == 0)&lt;br /&gt;
         res.size--;&lt;br /&gt;
     return res;&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;
| width=&amp;quot;50%&amp;quot;  |&lt;br /&gt;
 BigInteger operator % (const BigInteger &amp;amp;that) const {&lt;br /&gt;
     BigInteger carry;&lt;br /&gt;
     for (int i = size - 1; i &amp;gt;= 0; i--) {&lt;br /&gt;
         carry = carry * BASE;&lt;br /&gt;
         carry.d[0] = d[i];&lt;br /&gt;
         int l = 0, r = BASE - 1, m;&lt;br /&gt;
         while (l + 1 &amp;lt; r) {&lt;br /&gt;
             m = l + (r - l) / 2;&lt;br /&gt;
             if ((that * m).cmp(carry) &amp;lt;= 0)&lt;br /&gt;
                 l = m;&lt;br /&gt;
             else&lt;br /&gt;
                 r = m;&lt;br /&gt;
         }&lt;br /&gt;
         int digit = (that * r).cmp(carry) &amp;lt;= 0 ? r : l;&lt;br /&gt;
         carry = carry - that * digit;&lt;br /&gt;
     }&lt;br /&gt;
     return carry;&lt;br /&gt;
 }&lt;br /&gt;
| width=&amp;quot;10px&amp;quot; |&lt;br /&gt;
| width=&amp;quot;50%&amp;quot;  |&lt;br /&gt;
 int operator % (int that) const {&lt;br /&gt;
     int carry = 0;&lt;br /&gt;
     for (int i = size - 1; i &amp;gt;= 0; i--)&lt;br /&gt;
         carry = (1LL * carry * BASE + d[i]) % that;&lt;br /&gt;
     return carry;&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;
|}&lt;br /&gt;
&lt;br /&gt;
== Длинная арифметика на vector ==&lt;br /&gt;
&lt;br /&gt;
 struct BigInteger {&lt;br /&gt;
     vector&amp;lt;int&amp;gt; digits;&lt;br /&gt;
     inline static const int BASE = 1e9;&lt;br /&gt;
     inline static const int DIGIT_WIDTH = 9;&lt;br /&gt;
 &lt;br /&gt;
     inline int digit(int index) const {&lt;br /&gt;
         return index &amp;lt; digits.size() ? digits[index] : 0;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     inline int &amp;amp;digit(int index) {&lt;br /&gt;
         if (digits.size() &amp;lt;= index)&lt;br /&gt;
             digits.resize(index + 1);&lt;br /&gt;
         return digits[index];&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     inline void removeZeros() {&lt;br /&gt;
         while (!digits.empty() &amp;amp;&amp;amp; !digits.back())&lt;br /&gt;
             digits.pop_back();&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     BigInteger(long long value = 0) {&lt;br /&gt;
         for (; value; value /= BASE)&lt;br /&gt;
             digits.push_back(value % BASE);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     BigInteger(const string &amp;amp;s) {&lt;br /&gt;
         for (int r = s.size() - 1; r &amp;gt;= 0; r -= DIGIT_WIDTH) {&lt;br /&gt;
             int l = max(r - DIGIT_WIDTH + 1, 0);&lt;br /&gt;
             digits.push_back(stoi(s.substr(l, r - l + 1)));&lt;br /&gt;
         }&lt;br /&gt;
         removeZeros();&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     bool operator &amp;lt; (const BigInteger &amp;amp;that) const {&lt;br /&gt;
         if (digits.size() != that.digits.size())&lt;br /&gt;
             return digits.size() &amp;lt; that.digits.size();&lt;br /&gt;
         for (int i = digits.size() - 1; i &amp;gt;= 0; i--)&lt;br /&gt;
             if (digit(i) != that.digit(i))&lt;br /&gt;
                 return digit(i) &amp;lt; that.digit(i);&lt;br /&gt;
         return 0;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     BigInteger operator + (const BigInteger &amp;amp;that) const {&lt;br /&gt;
         BigInteger res;&lt;br /&gt;
         for (int i = 0, carry = 0; i &amp;lt; digits.size() || i &amp;lt; that.digits.size() || carry; i++) {&lt;br /&gt;
             int cur = digit(i) + that.digit(i) + carry;&lt;br /&gt;
             res.digits.push_back(cur % BASE);&lt;br /&gt;
             carry = cur / BASE;&lt;br /&gt;
         }&lt;br /&gt;
         return res;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     BigInteger operator * (const BigInteger &amp;amp;that) const {&lt;br /&gt;
         BigInteger res;&lt;br /&gt;
         for (int i = 0, carry = 0; i &amp;lt; digits.size(); i++) {&lt;br /&gt;
             for (int j = 0; j &amp;lt; that.digits.size() || carry; j++) {&lt;br /&gt;
                 long long cur = res.digit(i + j) + 1LL * digit(i) * that.digit(j) + carry;&lt;br /&gt;
                 res.digit(i + j) = cur % BASE;&lt;br /&gt;
                 carry = cur / BASE;&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         res.removeZeros();&lt;br /&gt;
         return res;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     BigInteger operator / (int that) const {&lt;br /&gt;
         BigInteger res;&lt;br /&gt;
         for (int i = digits.size() - 1, carry = 0; i &amp;gt;= 0; i--) {&lt;br /&gt;
             long long cur = 1LL * carry * BASE + digit(i);&lt;br /&gt;
             res.digit(i) = cur / that;&lt;br /&gt;
             carry = cur % that;&lt;br /&gt;
         }&lt;br /&gt;
         res.removeZeros();&lt;br /&gt;
         return res;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     int operator % (int that) const {&lt;br /&gt;
         long long res = 0;&lt;br /&gt;
         for (int i = digits.size() - 1; i &amp;gt;= 0; i--)&lt;br /&gt;
             res = (res * BASE + digit(i)) % that;&lt;br /&gt;
         return res;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     friend istream &amp;amp;operator &amp;gt;&amp;gt; (istream &amp;amp;in, BigInteger &amp;amp;value) {&lt;br /&gt;
         string s;&lt;br /&gt;
         in &amp;gt;&amp;gt; s;&lt;br /&gt;
         value = BigInteger(s);&lt;br /&gt;
         return in;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     friend ostream &amp;amp;operator &amp;lt;&amp;lt; (ostream &amp;amp;out, const BigInteger &amp;amp;value) {&lt;br /&gt;
         if (value.digits.empty()) {&lt;br /&gt;
             out &amp;lt;&amp;lt; 0;&lt;br /&gt;
         } else {&lt;br /&gt;
             out &amp;lt;&amp;lt; value.digits.back();&lt;br /&gt;
             for (int i = (int)value.digits.size() - 2; i &amp;gt;= 0; i--) {&lt;br /&gt;
                 out.width(DIGIT_WIDTH);&lt;br /&gt;
                 out.fill(&#039;0&#039;);&lt;br /&gt;
                 out &amp;lt;&amp;lt; value.digits[i];&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         return out;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
== Ссылки на задачи ==&lt;br /&gt;
* [http://acmp.ru/?main=task&amp;amp;id_task=103 ACMP #103 &amp;amp;mdash; Снова A + B]&lt;br /&gt;
* [http://acmp.ru/?main=task&amp;amp;id_task=143 ACMP #143 &amp;amp;mdash; A - B]&lt;br /&gt;
* [http://acmp.ru/?main=task&amp;amp;id_task=144 ACMP #144 &amp;amp;mdash; A * B]&lt;br /&gt;
* [http://acmp.ru/?main=task&amp;amp;id_task=145 ACMP #145 &amp;amp;mdash; A div B]&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
* [[Сложная длинная арифметика]]&lt;br /&gt;
* [http://e-maxx.ru/algo/big_integer e-maxx.ru &amp;amp;mdash; Длинная арифметика]&lt;br /&gt;
* [http://cppalgo.blogspot.ru/2010/05/blog-post.html cppalgo.blogspot.ru &amp;amp;mdash; Длинная арифметика]&lt;br /&gt;
* [http://brestprog.neocities.org/lections/longarithmetics.html brestprog.neocities.org &amp;amp;mdash; Длинная арифметика]&lt;br /&gt;
* [http://informatics.mccme.ru/course/view.php?id=17 informatics.mccme.ru &amp;amp;mdash; Курс &amp;amp;laquo;Арифметика и числовые алгоритмы&amp;amp;raquo; &amp;amp;mdash; часть 5]&lt;br /&gt;
* [http://github.com/ADJA/algos/blob/master/NumberTheory/BigInt.cpp Algos &amp;amp;mdash; Structure implementing long arithmetic in C++]&lt;br /&gt;
&lt;br /&gt;
[[Category:Арифметические алгоритмы]]&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=%D0%94%D0%BB%D0%B8%D0%BD%D0%BD%D0%B0%D1%8F_%D0%B0%D1%80%D0%B8%D1%84%D0%BC%D0%B5%D1%82%D0%B8%D0%BA%D0%B0&amp;diff=2926</id>
		<title>Длинная арифметика</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=%D0%94%D0%BB%D0%B8%D0%BD%D0%BD%D0%B0%D1%8F_%D0%B0%D1%80%D0%B8%D1%84%D0%BC%D0%B5%D1%82%D0%B8%D0%BA%D0%B0&amp;diff=2926"/>
		<updated>2025-04-13T19:00:53Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: /* Длинная арифметика на vector */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Хранение, ввод и вывод ==&lt;br /&gt;
&lt;br /&gt;
 class BigInteger {&lt;br /&gt;
     static const int BASE = 1e9;&lt;br /&gt;
     static const int BASE_LEN = 9;&lt;br /&gt;
     static const int MAX_SIZE = 1e4;&lt;br /&gt;
     int d[MAX_SIZE];&lt;br /&gt;
     int size;&lt;br /&gt;
 public:&lt;br /&gt;
     BigInteger(long long val = 0) {&lt;br /&gt;
         for (int i = 0; i &amp;lt; MAX_SIZE; i++)&lt;br /&gt;
             d[i] = 0;&lt;br /&gt;
         size = 0;&lt;br /&gt;
         if (val == 0)&lt;br /&gt;
             d[size++] = 0;&lt;br /&gt;
         while (val &amp;gt; 0) {&lt;br /&gt;
             d[size++] = val % BASE;&lt;br /&gt;
             val /= BASE;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
     BigInteger(char val[]) {&lt;br /&gt;
         for (int i = 0; i &amp;lt; MAX_SIZE; i++)&lt;br /&gt;
             d[i] = 0;&lt;br /&gt;
         size = 0;&lt;br /&gt;
         for (int i = strlen(val) - 1; i &amp;gt;= 0; i -= BASE_LEN) {&lt;br /&gt;
             int digit = 0;&lt;br /&gt;
             for (int j = max(0, i - BASE_LEN + 1); j &amp;lt;= i; j++)&lt;br /&gt;
                 digit = digit * 10 + val[j] - &#039;0&#039;;&lt;br /&gt;
             d[size++] = digit;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
     void print() const {&lt;br /&gt;
         static char spec[] = &amp;quot;%00d&amp;quot;;&lt;br /&gt;
         spec[2] = BASE_LEN + &#039;0&#039;;&lt;br /&gt;
         printf(&amp;quot;%d&amp;quot;, d[size - 1]);&lt;br /&gt;
         for (int i = size - 2; i &amp;gt;= 0; i--)&lt;br /&gt;
             printf(spec, d[i]);&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
== Сравнение ==&lt;br /&gt;
&lt;br /&gt;
 int cmp(const BigInteger &amp;amp;that) const {&lt;br /&gt;
     if (size != that.size)&lt;br /&gt;
         return size &amp;lt; that.size ? -1 : 1;&lt;br /&gt;
     for (int i = size - 1; i &amp;gt;= 0; i--) {&lt;br /&gt;
         if (d[i] != that.d[i])&lt;br /&gt;
             return d[i] &amp;lt; that.d[i] ? -1 : 1;&lt;br /&gt;
     }&lt;br /&gt;
     return 0;&lt;br /&gt;
 } &lt;br /&gt;
 bool operator &amp;lt; (const BigInteger &amp;amp;that) const {&lt;br /&gt;
     return cmp(that) &amp;lt; 0;&lt;br /&gt;
 }&lt;br /&gt;
 bool operator == (const BigInteger &amp;amp;that) const {&lt;br /&gt;
     return cmp(that) == 0;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Сложение ==&lt;br /&gt;
&lt;br /&gt;
 BigInteger operator + (const BigInteger &amp;amp;that) const {&lt;br /&gt;
     BigInteger res;        &lt;br /&gt;
     int carry = 0;&lt;br /&gt;
     for (int i = 0; i &amp;lt; max(size, that.size) || carry != 0; i++) {&lt;br /&gt;
         long long x = 0LL + d[i] + that.d[i] + carry;&lt;br /&gt;
         res.d[i] = x % BASE;&lt;br /&gt;
         carry = x / BASE;&lt;br /&gt;
     }&lt;br /&gt;
     res.size = max(size, that.size) + 1;&lt;br /&gt;
     while (res.size &amp;gt; 1 &amp;amp;&amp;amp; res.d[res.size - 1] == 0)&lt;br /&gt;
         res.size--;&lt;br /&gt;
     return res;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Вычитание ==&lt;br /&gt;
&lt;br /&gt;
Предполагается, что уменьшаемое больше вычитаемого.&lt;br /&gt;
&lt;br /&gt;
 BigInteger operator - (const BigInteger &amp;amp;that) const {&lt;br /&gt;
     BigInteger res;&lt;br /&gt;
     int carry = 0;&lt;br /&gt;
     for (int i = 0; i &amp;lt; size || carry != 0; i++) {&lt;br /&gt;
         long long x = 0LL + d[i] - that.d[i] + carry;&lt;br /&gt;
         res.d[i] = (x + BASE) % BASE;&lt;br /&gt;
         carry = x &amp;lt; 0 ? -1 : 0;&lt;br /&gt;
     }&lt;br /&gt;
     res.size = size;&lt;br /&gt;
     while (res.size &amp;gt; 1 &amp;amp;&amp;amp; res.d[res.size - 1] == 0)&lt;br /&gt;
         res.size--;&lt;br /&gt;
     return res;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Умножение ==&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
| width=&amp;quot;50%&amp;quot;  |&lt;br /&gt;
 BigInteger operator * (const BigInteger &amp;amp;that) const {&lt;br /&gt;
     BigInteger res;        &lt;br /&gt;
     for (int i = 0; i &amp;lt; size; i++) {&lt;br /&gt;
         int carry = 0;&lt;br /&gt;
         for (int j = 0; j &amp;lt; that.size || carry != 0; j++) {&lt;br /&gt;
             long long x = res.d[i + j] + 1LL * d[i] * that.d[j] + carry;&lt;br /&gt;
             res.d[i + j] = x % BASE;&lt;br /&gt;
             carry = x / BASE;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
     res.size = size + that.size;&lt;br /&gt;
     while (res.size &amp;gt; 1 &amp;amp;&amp;amp; res.d[res.size - 1] == 0)&lt;br /&gt;
         res.size--;&lt;br /&gt;
     return res;&lt;br /&gt;
 }&lt;br /&gt;
| width=&amp;quot;10px&amp;quot; |&lt;br /&gt;
| width=&amp;quot;50%&amp;quot;  |&lt;br /&gt;
 BigInteger operator * (int that) const {&lt;br /&gt;
     BigInteger res;        &lt;br /&gt;
     int carry = 0;&lt;br /&gt;
     for (int i = 0; i &amp;lt; size || carry != 0; i++) {&lt;br /&gt;
         long long x = 1LL * d[i] * that + carry;&lt;br /&gt;
         res.d[i] = x % BASE;&lt;br /&gt;
         carry = x / BASE;&lt;br /&gt;
     }&lt;br /&gt;
     res.size = size + 1;&lt;br /&gt;
     while (res.size &amp;gt; 1 &amp;amp;&amp;amp; res.d[res.size - 1] == 0)&lt;br /&gt;
         res.size--;&lt;br /&gt;
     return res;&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;
| width=&amp;quot;50%&amp;quot;  |&lt;br /&gt;
 BigInteger operator / (const BigInteger &amp;amp;that) const {&lt;br /&gt;
     BigInteger res, carry;&lt;br /&gt;
     for (int i = size - 1; i &amp;gt;= 0; i--) {&lt;br /&gt;
         carry = carry * BASE;&lt;br /&gt;
         carry.d[0] = d[i];&lt;br /&gt;
         int l = 0, r = BASE - 1, m;&lt;br /&gt;
         while (l + 1 &amp;lt; r) {&lt;br /&gt;
             m = l + (r - l) / 2;&lt;br /&gt;
             if ((that * m).cmp(carry) &amp;lt;= 0)&lt;br /&gt;
                 l = m;&lt;br /&gt;
             else&lt;br /&gt;
                 r = m;&lt;br /&gt;
         }&lt;br /&gt;
         res.d[i] = (that * r).cmp(carry) &amp;lt;= 0 ? r : l;&lt;br /&gt;
         carry = carry - that * res.d[i];&lt;br /&gt;
     }    &lt;br /&gt;
     res.size = size;&lt;br /&gt;
     while (res.size &amp;gt; 1 &amp;amp;&amp;amp; res.d[res.size - 1] == 0)&lt;br /&gt;
         res.size--;&lt;br /&gt;
     return res;&lt;br /&gt;
 }&lt;br /&gt;
| width=&amp;quot;10px&amp;quot; |&lt;br /&gt;
| width=&amp;quot;50%&amp;quot;  |&lt;br /&gt;
 BigInteger operator / (int that) const {&lt;br /&gt;
     BigInteger res;&lt;br /&gt;
     int carry = 0;&lt;br /&gt;
     for (int i = size - 1; i &amp;gt;= 0; i--) {&lt;br /&gt;
         long long x = 1LL * carry * BASE + d[i];&lt;br /&gt;
         res.d[i] = x / that;&lt;br /&gt;
         carry = x % that;&lt;br /&gt;
     }&lt;br /&gt;
     res.size = size;&lt;br /&gt;
     while (res.size &amp;gt; 1 &amp;amp;&amp;amp; res.d[res.size - 1] == 0)&lt;br /&gt;
         res.size--;&lt;br /&gt;
     return res;&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;
| width=&amp;quot;50%&amp;quot;  |&lt;br /&gt;
 BigInteger operator % (const BigInteger &amp;amp;that) const {&lt;br /&gt;
     BigInteger carry;&lt;br /&gt;
     for (int i = size - 1; i &amp;gt;= 0; i--) {&lt;br /&gt;
         carry = carry * BASE;&lt;br /&gt;
         carry.d[0] = d[i];&lt;br /&gt;
         int l = 0, r = BASE - 1, m;&lt;br /&gt;
         while (l + 1 &amp;lt; r) {&lt;br /&gt;
             m = l + (r - l) / 2;&lt;br /&gt;
             if ((that * m).cmp(carry) &amp;lt;= 0)&lt;br /&gt;
                 l = m;&lt;br /&gt;
             else&lt;br /&gt;
                 r = m;&lt;br /&gt;
         }&lt;br /&gt;
         int digit = (that * r).cmp(carry) &amp;lt;= 0 ? r : l;&lt;br /&gt;
         carry = carry - that * digit;&lt;br /&gt;
     }&lt;br /&gt;
     return carry;&lt;br /&gt;
 }&lt;br /&gt;
| width=&amp;quot;10px&amp;quot; |&lt;br /&gt;
| width=&amp;quot;50%&amp;quot;  |&lt;br /&gt;
 int operator % (int that) const {&lt;br /&gt;
     int carry = 0;&lt;br /&gt;
     for (int i = size - 1; i &amp;gt;= 0; i--)&lt;br /&gt;
         carry = (1LL * carry * BASE + d[i]) % that;&lt;br /&gt;
     return carry;&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;
|}&lt;br /&gt;
&lt;br /&gt;
== Длинная арифметика на vector ==&lt;br /&gt;
&lt;br /&gt;
 struct BigInteger {&lt;br /&gt;
     vector&amp;lt;int&amp;gt; digits;&lt;br /&gt;
     inline static const int BASE = 1e9;&lt;br /&gt;
     inline static const int DIGIT_WIDTH = 9;&lt;br /&gt;
 &lt;br /&gt;
     inline int digit(int index) const {&lt;br /&gt;
         return index &amp;lt; digits.size() ? digits[index] : 0;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     inline int &amp;amp;digit(int index) {&lt;br /&gt;
         if (digits.size() &amp;lt;= index)&lt;br /&gt;
             digits.resize(index + 1);&lt;br /&gt;
         return digits[index];&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     inline void removeZeros() {&lt;br /&gt;
         while (!digits.empty() &amp;amp;&amp;amp; !digits.back())&lt;br /&gt;
             digits.pop_back();&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     BigInteger(long long value = 0) {&lt;br /&gt;
         for (; value; value /= BASE)&lt;br /&gt;
             digits.push_back(value % BASE);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     BigInteger(const string &amp;amp;s) {&lt;br /&gt;
         for (int r = s.size() - 1; r &amp;gt;= 0; r -= DIGIT_WIDTH) {&lt;br /&gt;
             int l = max(r - DIGIT_WIDTH + 1, 0);&lt;br /&gt;
             digits.push_back(stoi(s.substr(l, r - l + 1)));&lt;br /&gt;
         }&lt;br /&gt;
         removeZeros();&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
     bool operator &amp;lt; (const BigInteger &amp;amp;that) const {&lt;br /&gt;
         if (digits.size() != that.digits.size())&lt;br /&gt;
             return digits.size() &amp;lt; that.digits.size();&lt;br /&gt;
         for (int i = digits.size() - 1; i &amp;gt;= 0; i--)&lt;br /&gt;
             if (digit(i) != that.digit(i))&lt;br /&gt;
                 return digit(i) &amp;lt; that.digit(i);&lt;br /&gt;
         return 0;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     BigInteger operator + (const BigInteger &amp;amp;that) const {&lt;br /&gt;
         BigInteger res;&lt;br /&gt;
         for (int i = 0, carry = 0; i &amp;lt; digits.size() || i &amp;lt; that.digits.size() || carry; i++) {&lt;br /&gt;
             int cur = digit(i) + that.digit(i) + carry;&lt;br /&gt;
             res.digits.push_back(cur % BASE);&lt;br /&gt;
             carry = cur / BASE;&lt;br /&gt;
         }&lt;br /&gt;
         return res;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     BigInteger operator * (const BigInteger &amp;amp;that) const {&lt;br /&gt;
         BigInteger res;&lt;br /&gt;
         for (int i = 0, carry = 0; i &amp;lt; digits.size(); i++) {&lt;br /&gt;
             for (int j = 0; j &amp;lt; that.digits.size() || carry; j++) {&lt;br /&gt;
                 long long cur = res.digit(i + j) + 1LL * digit(i) * that.digit(j) + carry;&lt;br /&gt;
                 res.digit(i + j) = cur % BASE;&lt;br /&gt;
                 carry = cur / BASE;&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         res.removeZeros();&lt;br /&gt;
         return res;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     BigInteger operator / (int that) const {&lt;br /&gt;
         BigInteger res;&lt;br /&gt;
         for (int i = digits.size() - 1, carry = 0; i &amp;gt;= 0; i--) {&lt;br /&gt;
             long long cur = 1LL * carry * BASE + digit(i);&lt;br /&gt;
             res.digit(i) = cur / that;&lt;br /&gt;
             carry = cur % that;&lt;br /&gt;
         }&lt;br /&gt;
         res.removeZeros();&lt;br /&gt;
         return res;&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
     int operator % (int that) const {&lt;br /&gt;
         long long res = 0;&lt;br /&gt;
         for (int i = digits.size() - 1; i &amp;gt;= 0; i--)&lt;br /&gt;
             res = (res * BASE + digit(i)) % that;&lt;br /&gt;
         return res;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     friend istream &amp;amp;operator &amp;gt;&amp;gt; (istream &amp;amp;in, BigInteger &amp;amp;value) {&lt;br /&gt;
         string s;&lt;br /&gt;
         in &amp;gt;&amp;gt; s;&lt;br /&gt;
         value = BigInteger(s);&lt;br /&gt;
         return in;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     friend ostream &amp;amp;operator &amp;lt;&amp;lt; (ostream &amp;amp;out, const BigInteger &amp;amp;value) {&lt;br /&gt;
         if (value.digits.empty()) {&lt;br /&gt;
             out &amp;lt;&amp;lt; 0;&lt;br /&gt;
         } else {&lt;br /&gt;
             out &amp;lt;&amp;lt; value.digits.back();&lt;br /&gt;
             for (int i = (int)value.digits.size() - 2; i &amp;gt;= 0; i--) {&lt;br /&gt;
                 out.width(DIGIT_WIDTH);&lt;br /&gt;
                 out.fill(&#039;0&#039;);&lt;br /&gt;
                 out &amp;lt;&amp;lt; value.digits[i];&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         return out;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
== Ссылки на задачи ==&lt;br /&gt;
* [http://acmp.ru/?main=task&amp;amp;id_task=103 ACMP #103 &amp;amp;mdash; Снова A + B]&lt;br /&gt;
* [http://acmp.ru/?main=task&amp;amp;id_task=143 ACMP #143 &amp;amp;mdash; A - B]&lt;br /&gt;
* [http://acmp.ru/?main=task&amp;amp;id_task=144 ACMP #144 &amp;amp;mdash; A * B]&lt;br /&gt;
* [http://acmp.ru/?main=task&amp;amp;id_task=145 ACMP #145 &amp;amp;mdash; A div B]&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
* [[Сложная длинная арифметика]]&lt;br /&gt;
* [http://e-maxx.ru/algo/big_integer e-maxx.ru &amp;amp;mdash; Длинная арифметика]&lt;br /&gt;
* [http://cppalgo.blogspot.ru/2010/05/blog-post.html cppalgo.blogspot.ru &amp;amp;mdash; Длинная арифметика]&lt;br /&gt;
* [http://brestprog.neocities.org/lections/longarithmetics.html brestprog.neocities.org &amp;amp;mdash; Длинная арифметика]&lt;br /&gt;
* [http://informatics.mccme.ru/course/view.php?id=17 informatics.mccme.ru &amp;amp;mdash; Курс &amp;amp;laquo;Арифметика и числовые алгоритмы&amp;amp;raquo; &amp;amp;mdash; часть 5]&lt;br /&gt;
* [http://github.com/ADJA/algos/blob/master/NumberTheory/BigInt.cpp Algos &amp;amp;mdash; Structure implementing long arithmetic in C++]&lt;br /&gt;
&lt;br /&gt;
[[Category:Арифметические алгоритмы]]&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=%D0%9A%D0%BD%D0%B8%D0%B3%D0%B8_%D0%B8_%D1%81%D0%B0%D0%B9%D1%82%D1%8B&amp;diff=2925</id>
		<title>Книги и сайты</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=%D0%9A%D0%BD%D0%B8%D0%B3%D0%B8_%D0%B8_%D1%81%D0%B0%D0%B9%D1%82%D1%8B&amp;diff=2925"/>
		<updated>2025-02-21T18:54:22Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Тестирующие системы ==&lt;br /&gt;
Представленные в [http://github.com/vfolunin/monitor мониторе]:&lt;br /&gt;
* [http://acmp.ru acmp.ru]&lt;br /&gt;
* [http://informatics.msk.ru informatics.msk.ru]&lt;br /&gt;
* [http://acm.timus.ru acm.timus.ru]&lt;br /&gt;
* [http://codeforces.com codeforces.com]&lt;br /&gt;
* [http://e-olymp.com e-olymp.com]&lt;br /&gt;
* [http://spoj.com spoj.com]&lt;br /&gt;
* [http://onlinejudge.org onlinejudge.org]&lt;br /&gt;
* [http://cses.fi/problemset cses.fi/problemset]&lt;br /&gt;
Другие:&lt;br /&gt;
* [http://open.kattis.com open.kattis.com]&lt;br /&gt;
* [http://beecrowd.com.br beecrowd.com.br]&lt;br /&gt;
* [http://leetcode.com leetcode.com]&lt;br /&gt;
&lt;br /&gt;
== Сайты ==&lt;br /&gt;
Теория:&lt;br /&gt;
* [http://e-maxx.ru/algo/ e-maxx.ru], [http://cp-algorithms.com cp-algorithms.com]&lt;br /&gt;
* [http://neerc.ifmo.ru/wiki/index.php?title=%D0%94%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%B0%D1%8F_%D0%BC%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0,_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D1%8B_%D0%B8_%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D1%8B_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85 neerc.ifmo.ru/wiki]&lt;br /&gt;
* [http://brestprog.by/topics/ brestprog.by]&lt;br /&gt;
* [https://ru.algorithmica.org/ ru.algorithmica.org], [http://algorithmica.org/ru/ algorithmica.org/ru], [http://algorithmica.org/tg/ algorithmica.org/tg], [https://wiki.algocode.ru/index.php?title=%D0%A1%D0%BF%D0%B8%D1%81%D0%BE%D0%BA_%D0%B2%D1%81%D0%B5%D1%85_%D0%BA%D0%BE%D0%BD%D1%81%D0%BF%D0%B5%D0%BA%D1%82%D0%BE%D0%B2 wiki.algocode.ru]&lt;br /&gt;
* [https://codeforces.com/catalog codeforces.com/catalog]&lt;br /&gt;
* [https://usaco.guide usaco.guide]&lt;br /&gt;
* [http://www.topcoder.com/community/data-science/data-science-tutorials/ topcoder.com]&lt;br /&gt;
Демонстрации:&lt;br /&gt;
* [http://visualgo.net/ VisuAlgo]&lt;br /&gt;
* [http://www.cs.usfca.edu/~galles/visualization/Algorithms.html Data Structure Visualizations]&lt;br /&gt;
Код:&lt;br /&gt;
* [http://github.com/indy256/codelibrary indy256&#039;s CodeLibrary (GitHub)]&lt;br /&gt;
* [http://github.com/ADJA/algos ADJA&#039;s Algos (GitHub)]&lt;br /&gt;
* [http://algs4.cs.princeton.edu/code/ R. Sedgewick and K. Wayne&#039;s Java Algorithms and Clients] ([http://github.com/kevin-wayne/algs4/ GitHub])&lt;br /&gt;
&lt;br /&gt;
== Книги ==&lt;br /&gt;
=== Алгоритмы (классические учебники) ===&lt;br /&gt;
* Introduction to Algorithms / Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, Clifford Stein. — 4th ed. — Cambridge, MA: MIT Press, 2022. — 1312 p. ([http://mitpress.mit.edu/9780262046305/introduction-to-algorithms/ сайт книги])&lt;br /&gt;
: Алгоритмы: построение и анализ / Томас Х. Кормен, Чарльз И. Лейзерсон, Рональд Л. Ривест, Клиффорд Штайн. — 3-е изд. — М.: Вильямс, 2013. — 1328 с.&lt;br /&gt;
* Sedgewick R., Wayne K. Algorithms / Robert Sedgewick, Kevin Wayne. — 4th ed. — Boston, MA: Addison-Wesley, 2011. — 976 p. ([http://algs4.cs.princeton.edu сайт книги])&lt;br /&gt;
: Седжвик Р., Уэйн К. Алгоритмы на Java / Роберт Седжвик, Кевин Уэйн. — 4-е изд. — М.: Вильямс, 2013. — 848 с.&lt;br /&gt;
* Skiena S. The Algorithm Design Manual / Steven S. Skiena. — 3rd ed. — Cham: Springer, 2020. — 810 p. ([http://www.algorist.com сайт книги])&lt;br /&gt;
: Скиена С. Алгоритмы. Руководство по разработке / Стивен С. Скиена. — 2-е изд. — СПб.: БХВ-Петербург, 2011. — 720 с.&lt;br /&gt;
&lt;br /&gt;
=== Олимпиадное программирование ===&lt;br /&gt;
* [https://www.comp.nus.edu.sg/~stevenha/database/Art_of_Programming_Contest_SE_for_uva.pdf Art of Programming Contest / Ed. by A. S. Arefin. — 2nd ed. — Dhaka: Gyankosh Prokashoni, 2006. — 247 p.]&lt;br /&gt;
* {{GoodBadge}} [https://github.com/prasadgujar/CompetitiveProgramming/blob/master/book/Competitive%20Programming%203.pdf Halim S., Halim F. Competitive Programming 3 / Steven Halim, Felix Halim. — Raleigh, NC: Lulu, 2013. — 448 p.]&lt;br /&gt;
* Halim S., Halim F., Effendy S. Competitive Programming 4. Book 1 / Steven Halim, Felix Halim, Suhendry Effendy. — Lulu, 2020. — 329 p.&lt;br /&gt;
* Halim S., Halim F., Effendy S. Competitive Programming 4. Book 2 / Steven Halim, Felix Halim, Suhendry Effendy. — Lulu, 2020. — 352 p.&lt;br /&gt;
* {{GoodBadge}} [https://github.com/pllk/cphb/blob/master/book.pdf Laaksonen A. Competitive Programmer’s Handbook / Antti Laaksonen. — 2019. — 300 p.]&lt;br /&gt;
* [http://darrenyao.com/usacobook/cpp.pdf Yao D. An Introduction to the USA Computing Olympiad / Darren Yao. — 2020. — 87 p.]&lt;br /&gt;
* Долинский М. С. Решение сложных и олимпиадных задач по программированию / М. С. Долинский. — СПб.: Питер, 2006. — 366 с.&lt;br /&gt;
* {{GoodBadge}} Лааксонен А. Олимпиадное программирование. / Антти Лааксонен. — 2-е изд. — М.: ДМК Пресс, 2020. — 328 с. {{Comment|Бумажный вариант online-книги того же автора. Порядок глав изменён; добавлены разделы про центроид-декомпозицию, heavy-light-декомпозицию, деревья поиска в глубину, суффиксные массивы, декартовы деревья, оптимизации ДП, параллельный бинпоиск, динамическую связность, FFT, MCMF.}}&lt;br /&gt;
* Окулов С. М. Основы программирования / С. М. Окулов. — 4-е изд. — М.: БИНОМ, 2008. — 440 с.&lt;br /&gt;
* Окулов С. М. Программирование в алгоритмах / С. М. Окулов. — 3-е изд. — М.: БИНОМ, 2007. — 383 с.&lt;br /&gt;
* {{GoodBadge}} Порублев И. Н., Ставровский А. Б. Алгоритмы и программы. Решение олимпиадных задач. — М.: Вильямс, 2007. — 480 с.&lt;br /&gt;
* Скиена С. С., Ревилла М. А. Олимпиадные задачи по программированию. Руководство по подготовке к соревнованиям / С. С. Скиена, М. А. Ревилла. — М.: КУДИЦ-ОБРАЗ, 2005. — 416 с.&lt;br /&gt;
&lt;br /&gt;
=== Олимпиадное программирование (более сложные темы) ===&lt;br /&gt;
* [https://kostka.dev/sp/spbook.pdf Kostka B. Sports Programming in Practice / Bartosz Kostka — University of Wroclaw, 2021. — 234 p.]&lt;br /&gt;
* [https://peltorator.ru/cp_book.pdf Горбачёв Е. А. Спортивное программирование. Алгоритмы и структуры данных / Е. А. Горбачёв — 2021. — 136 с.]&lt;br /&gt;
&lt;br /&gt;
=== Сборники задач ===&lt;br /&gt;
* Durr C., Vie J. Competitive Programming in Python / Christoph Durr, Jill-Jenn Vie. — Cambridge University Press, 2021. — 264 p.&lt;br /&gt;
* Wu Y., Wang J. Algorithm Design Practice for Collegiate Programming Contests and Education / Yonghui Wu, Jiande Wang. — CRC Press, 2018. — 706 p.&lt;br /&gt;
* Wu Y., Wang J. Data Structure Practice for Collegiate Programming Contests and Education / Yonghui Wu, Jiande Wang. — CRC Press, 2016. — 512 p.&lt;br /&gt;
* [http://acmp.ru/download/book.pdf Алексеев А. В., Беляев С. Н. Подготовка школьников к олимпиадам по информатике с использованием веб-сайта / А. В. Алексеев, С. Н. Беляев. — Ханты-Мансийск: РИО ИРО, 2008. — 284 с.]&lt;br /&gt;
* Брудно А. Л., Каплан Л. И. Московские олимпиады по программированию / Под ред. Б. Н. Наумова. — 2-е изд. — М.: Наука, 1990. — 208 с.&lt;br /&gt;
* Кирюхин В. М., Окулов С. М. Методика решения задач по информатике. Международные олимпиады / В. М. Кирюхин, С. М. Окулов. — М.: БИНОМ, 2007. — 600 с.&lt;br /&gt;
* [https://www.olympiads.ru/books/mskolymp/mosinformatolymp.pdf Московские олимпиады по информатике / Под ред. Е. В. Андреевой, В. М. Гуровица и В. А. Матюхина. — М.: МЦНМО, 2006. — 256 с.]&lt;br /&gt;
* [https://www.olympiads.ru/moscow/sbory/sbory2.pdf Московские учебно тренировочные сборы по информатике. Весна&amp;amp;ndash;2006 / Под ред. В. М. Гуровица. — М.: МЦНМО, 2007. — 194 с.]&lt;br /&gt;
* Меньшиков Ф. В. Олимпиадные задачи по программированию / Ф. В. Меньшиков. — СПб: Питер, 2006. — 315 с.&lt;br /&gt;
* [http://onzi.narod.ru Особенности национальных задач по информатике / В. И. Беров, А. В. Лапунов, В. А. Матюхин, А. Е. Пономарёв. — Киров: Триада-С, 2000. — 160 с.]&lt;br /&gt;
* [http://www.mccme.ru/free-books/shen/shen-progbook.pdf Шень А. Программирование: теоремы и задачи / А. Шень. — 6-е изд. — М.: МЦНМО, 2017. — 320 с.]&lt;br /&gt;
&lt;br /&gt;
=== Литература по темам ===&lt;br /&gt;
==== Алгоритмы ====&lt;br /&gt;
* [http://jeffe.cs.illinois.edu/teaching/algorithms Erickson J. Algorithms / Jeff Erickson. — Urbana, IL: University of Illinois at Urbana-Champaign, 2019. — 472 p.]&lt;br /&gt;
* [http://www.algorithmsilluminated.org Roughgarden T. Algorithms Illuminated]:&lt;br /&gt;
:* Roughgarden T. Algorithms Illuminated. Part 1: The Basics / Tim Roughgarden. — San-Francisco, CA: Soundlikeyourself Publishing, 2017. — 216 p.&lt;br /&gt;
:* Roughgarden T. Algorithms Illuminated. Part 2: Graph Algorithms and Data Structures / Tim Roughgarden. — San-Francisco, CA: Soundlikeyourself Publishing, 2018. — 221 p.&lt;br /&gt;
:* Roughgarden T. Algorithms Illuminated. Part 3: Greedy Algorithms and Dynamic Programming / Tim Roughgarden. — New York, NY: Soundlikeyourself Publishing, 2019. — 229 p.&lt;br /&gt;
:* Roughgarden T. Algorithms Illuminated. Part 4:  Algorithms for NP-Hard Problems / Tim Roughgarden. — New York, NY: Soundlikeyourself Publishing, 2020. — 271 p.&lt;br /&gt;
* Sedgewick R., Flajolet P. An Introduction to the Analysis of Algorithms / Robert Sedgewick, Philippe Flajolet. — 2th ed. — Boston, MA: Addison-Wesley, 2013. — 592 p.&lt;br /&gt;
* Ахо А., Хопкрофт Д., Ульман Д. Построение и анализ вычислительных алгоритмов / А. Ахо, Дж. Хопкрофт, Дж. Ульман. — М.: Мир, 1979. — 536 с.&lt;br /&gt;
* Ахо А., Хопкрофт Д., Ульман Д. Структуры данных и алгоритмы / Альфред В. Ахо, Джон Хопкрофт, Джеффри Д. Ульман. — М.: Вильямс, 2000. — 384 с.&lt;br /&gt;
* Бабенко М. А., Левин М. В. Введение в теорию алгоритмов и структур данных / М. А. Бабенко, М. В. Левин. — М.: МЦНМО, 2016. — 144 с. {{Comment|Темы: динамические массивы, амортизационный анализ; сортировка выбором, &amp;amp;Omega;-оценка для сортировок сравнением, mergesort, quicksort, порядковая статистика (за O(N) в среднем и худшем случае); линейный поиск, бинарный поиск, деревья поиска, splay-деревья; кучи, heapsort, k-ичные кучи, сливаемые кучи (leftist и skew), персистентность, декартовы деревья; хеширование, разрешение коллизий (цепочки, открытая адресация, двойное хеширование), универсальное хеширование, совершенное хеширование, фильтр Блюма; DSU, DSU с отменами; RMQ, деревья отрезков, разреженные таблицы, LCA, сведение LCA к RMQ и наоборот, RMQ и LCA за (O(N), O(1)); динамическое программирование, НВП, перемножение цепочки матриц, принципы ДП.}}&lt;br /&gt;
* Вирт Н. Алгоритмы и структуры данных / Н. Вирт. — М.: Мир, 1989. — 360 с.&lt;br /&gt;
* [http://alexanderskulikov.github.io/files/algorithms_href.pdf Дасгупта С., Пападимитриу Х., Вазирани У. Алгоритмы / С. Дасгупта, Х. Пападимитриу, У. Вазирани. — М.: МЦНМО, 2014. — 320 с.]&lt;br /&gt;
* Клейнберг Дж., Тардос Е. Алгоритмы: разработка и применение / Джон Клейнберг, Ева Тардос. — СПб.: Питер, 2016. — 800 с. ([http://www.cs.princeton.edu/~wayne/kleinberg-tardos слайды лекций])&lt;br /&gt;
* Левитин А. В. Алгоритмы: введение в разработку и анализ / Ананий В. Левитин. — М.: Вильямс, 2006. — 576 с.&lt;br /&gt;
* Макконнелл Дж. Анализ алгоритмов. Вводный курс / Дж. Макконнелл. — М.: Техносфера, 2002. — 304 с.&lt;br /&gt;
* Стивенс Р. Алгоритмы. Теория и практическое применение / Род Стивенс. — М.: Э, 2016. — 544 с.&lt;br /&gt;
&lt;br /&gt;
==== Структуры данных ====&lt;br /&gt;
* Brass P. Advanced Data Structures / Peter Brass. — Cambridge: Cambridge University Press, 2008. — 472 pp.&lt;br /&gt;
* [https://opendatastructures.org Morin P. Open Data Structures / Par Morin. — Edmonton, AB: Athabasca University Press, 2013. — 344 pp.]&lt;br /&gt;
&lt;br /&gt;
==== Дискретная математика ====&lt;br /&gt;
* Асанов М. О. Баранский В. А., Расин В. В. Дискретная математика: графы, матроиды, алгоритмы / М. О. Асанов, В. А. Баранский, В. В. Расин. — 3-е изд. — СПб.: Лань, 2020. — 364 с.&lt;br /&gt;
* Грэхем Р., Кнут Д., Паташник О. Конкретная математика. Основание информатики / Р. Грэхем, Д. Кнут, О. Паташник. — М.: Мир, 1998. — 703 с.&lt;br /&gt;
* Кук Д., Бейз Г. Компьютерная математика / Д. Кук, Г. Бейз. — М.: Наука, 1990. — 384 с.&lt;br /&gt;
* Новиков Ф. А. Дискретная математика для программистов / Ф. А. Новиков. — 2-е изд. — СПб.: Питер. 2007. — 364 с.&lt;br /&gt;
* Окулов С. М. Дискретная математика. Теория и практика решения задач по информатике / С. М. Окулов. — 2-е изд. — М.: БИНОМ, 2012. — 422 с.&lt;br /&gt;
* Хаггарти Р. Дискретная математика для программистов / Р. Хаггарти — М.: Техносфера, 2004. — 320 с.&lt;br /&gt;
&lt;br /&gt;
==== Динамическое программирование ====&lt;br /&gt;
* Kellerer H., Pferschy U., Pisinger D. Knapsack Problems / Hans Kellerer, Ulrich Pferschy, David Pisinger. — Berlin: Springer-Verlag, 2004. — 568 p.&lt;br /&gt;
* [http://www.or.deis.unibo.it/kp/KnapsackProblems.pdf Martello S., Toth P. Knapsack problems / Silvano Martello, Paolo Toth. — New York, NY: Wiley, 1990. — 308 p.]&lt;br /&gt;
* Окулов С. М., Пестов О. А. Динамическое программирование / С. М. Окулов, О. А. Пестов. — М.: Бином, 2012. — 296 с.&lt;br /&gt;
&lt;br /&gt;
==== Вычислительная геометрия ====&lt;br /&gt;
* [http://vlecomte.github.io/cp-geo.pdf Lecomte V. Handbook of geometry for competitive programmers / Victor Lecomte. — 2018. — 122 p.]&lt;br /&gt;
* {{GoodBadge}} [http://e-maxx.ru/bookz/files/andreeva.pdf Андреева Е. В., Егоров Ю. Е. Вычислительная геометрия на плоскости / Е. В. Андреева, Ю. Е. Егоров. // Информатика. — 2002. — №39, 40, 43, 44]&lt;br /&gt;
* Берг М. Вычислительная геометрия. Алгоритмы и приложения / М. Берг, О. Чеонг, М. Кревельд, М. Овермарс. — М.: ДМК-Пресс, 2016. — 438 с.&lt;br /&gt;
* Препарата Ф., Шеймос М. Вычислительная геометрия / Ф. Препарата, М. Шеймос. — М.: Мир, 1989. — 478 с.&lt;br /&gt;
* [https://server.179.ru/tasks/python/2014b1/minimum.pdf Теоретический минимум по по вычислительной геометрии для групп параллели B’. — Летняя компьютерная школа, 2010 г. — 6 с.]&lt;br /&gt;
&lt;br /&gt;
==== Теория графов ====&lt;br /&gt;
* Кристофидес Н. Теория графов. Алгоритмический подход / Н. Кристофидес. — М.: Мир, 1978. — 432 с.&lt;br /&gt;
* Оре О. Графы и их применение / О. Оре. — М. Мир, 1965. — 175 с.&lt;br /&gt;
* Оре О. Теория графов / О. Оре. — 2-е изд. — М. Наука, 1980. — 336 с.&lt;br /&gt;
* Харари Ф. Теория графов / Под ред. Г. П. Гаврилова. — М.: Едиториал УРСС, 2003. — 296 с.&lt;br /&gt;
&lt;br /&gt;
==== Алгоритмы на строках ====&lt;br /&gt;
* Гасфилд Д. Строки, деревья и последовательности в алгоритмах / Дэн Гасфилд. — СПб: Невский диалект; БХВ-Петербург, 2003. — 654 с.&lt;br /&gt;
* Смит Б. Методы и алгоритмы вычислений на строках / Билл Смит. — М.: Вильямс, 2006. — 496 с.&lt;br /&gt;
&lt;br /&gt;
== Периодические издания и сборники ==&lt;br /&gt;
* Журнал [http://ioinformatics.org/page/ioi-journal-contents/3 Olympiads in Informatics]&lt;br /&gt;
: [http://ioinformatics.org/page/ioi-journal-index/44#volume1 2007], [http://ioinformatics.org/page/ioi-journal-index/44#volume2 2008], [http://ioinformatics.org/page/ioi-journal-index/44#volume3 2009], [http://ioinformatics.org/page/ioi-journal-index/44#volume4 2010], [http://ioinformatics.org/page/ioi-journal-index/44#volume5 2011], [http://ioinformatics.org/page/ioi-journal-index/44#volume6 2012], [http://ioinformatics.org/page/ioi-journal-index/44#volume7 2013], [http://ioinformatics.org/page/ioi-journal-index/44#volume8 2014], [http://ioinformatics.org/page/ioi-journal-index/44#volume9 2015], [http://ioinformatics.org/page/ioi-journal-index/44#volume10 2016], [http://ioinformatics.org/page/ioi-journal-index/44#volume10si 2016+], [http://ioinformatics.org/page/ioi-journal-index/44#volume11 2017], [http://ioinformatics.org/page/ioi-journal-index/44#volume11si 2017+], [http://ioinformatics.org/page/ioi-journal-index/44#volume12 2018], [http://ioinformatics.org/page/ioi-journal-index/44#volume13 2019], [http://ioinformatics.org/page/ioi-journal-index/44#volume14 2020], [http://ioinformatics.org/page/ioi-journal-index/44#volume15 2021], [http://ioinformatics.org/page/ioi-journal-index/44#volume16 2022], [http://ioinformatics.org/page/ioi-journal-index/44#volume17 2023], [http://ioinformatics.org/page/ioi-journal-index/44#volume18 2024]&lt;br /&gt;
* Сборники Зимней школы по программированию ХНУРЭ&lt;br /&gt;
: [http://ws.kh.ua/media/sbornik/Sbornik2008.pdf 2008], [http://ws.kh.ua/media/sbornik/Sbornik2009.pdf 2009], [http://ws.kh.ua/media/sbornik/Sbornik2010.pdf 2010], [http://ws.kh.ua/media/sbornik/Sbornik2011.pdf 2011], [http://ws.kh.ua/media/sbornik/Sbornik2012.pdf 2012], [http://ws.kh.ua/media/sbornik/Sbornik2013.pdf 2013], [http://ws.kh.ua/media/sbornik/Sbornik2014.pdf 2014]&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B2%D0%BE%D0%B7%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B2_%D1%81%D1%82%D0%B5%D0%BF%D0%B5%D0%BD%D1%8C&amp;diff=2924</id>
		<title>Быстрое возведение в степень</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B2%D0%BE%D0%B7%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B2_%D1%81%D1%82%D0%B5%D0%BF%D0%B5%D0%BD%D1%8C&amp;diff=2924"/>
		<updated>2025-02-16T13:25:18Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Базовая реализация ==&lt;br /&gt;
&lt;br /&gt;
 long long binPow(long long x, long long p, long long mod) {&lt;br /&gt;
     if (!p)&lt;br /&gt;
         return 1 % mod;&lt;br /&gt;
     if (p % 2)&lt;br /&gt;
         return binPow(x, p - 1, mod) * x % mod;&lt;br /&gt;
     long long r = binPow(x, p / 2, mod);&lt;br /&gt;
     return r * r % mod;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Вычисление степеней, превышающих размер long long ==&lt;br /&gt;
&lt;br /&gt;
Если x и mod взаимно просты, то binPow(x, p, mod) == binPow(x, p % phi(mod), mod), где phi — функция Эйлера.&lt;br /&gt;
&lt;br /&gt;
Если x и mod не взаимно просты, но p &amp;amp;ge; log&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt;(mod), то binPow(x, p, mod) == binPow(x, p % phi(mod) + phi(mod), mod).&lt;br /&gt;
&lt;br /&gt;
 long long phi(long long n) {&lt;br /&gt;
     long long res = n;&lt;br /&gt;
     for (long long d = 2; d * d &amp;lt;= n; d++) {&lt;br /&gt;
         if (n % d == 0) {&lt;br /&gt;
             while (n % d == 0)&lt;br /&gt;
                 n /= d;&lt;br /&gt;
             res -= res / d;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
     if (n &amp;gt; 1)&lt;br /&gt;
         res -= res / n;&lt;br /&gt;
     return res;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Возведение в степень матриц ==&lt;br /&gt;
&lt;br /&gt;
 const long long MOD = 1e9 + 7;&lt;br /&gt;
 &lt;br /&gt;
 using Matrix = vector&amp;lt;vector&amp;lt;long long&amp;gt;&amp;gt;;&lt;br /&gt;
 &lt;br /&gt;
 Matrix identityMatrix(int size) {&lt;br /&gt;
     Matrix res(size, vector&amp;lt;long long&amp;gt;(size));&lt;br /&gt;
     for (int i = 0; i &amp;lt; res.size(); i++)&lt;br /&gt;
         res[i][i] = 1;&lt;br /&gt;
     return res;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 Matrix operator * (const Matrix &amp;amp;a, const Matrix &amp;amp;b) {&lt;br /&gt;
     Matrix res(a.size(), vector&amp;lt;long long&amp;gt;(b[0].size()));&lt;br /&gt;
     for (int y = 0; y &amp;lt; res.size(); y++)&lt;br /&gt;
         for (int x = 0; x &amp;lt; res[0].size(); x++)&lt;br /&gt;
             for (int i = 0; i &amp;lt; a[0].size(); i++)&lt;br /&gt;
                 res[y][x] = (res[y][x] + a[y][i] * b[i][x] % MOD) % MOD;&lt;br /&gt;
     return res;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 Matrix pow(const Matrix &amp;amp;m, long long p) {&lt;br /&gt;
     if (!p)&lt;br /&gt;
         return identityMatrix(m.size());&lt;br /&gt;
     if (p % 2)&lt;br /&gt;
         return pow(m, p - 1) * m;&lt;br /&gt;
     Matrix r = pow(m, p / 2);&lt;br /&gt;
     return r * r;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
* [http://e-maxx.ru/algo/binary_pow E-maxx — Бинарное возведение в степень]&lt;br /&gt;
* [https://cp-algorithms.com/algebra/binary-exp.html cp-algorithms.com — Binary Exponentiation]&lt;br /&gt;
* [https://cp-algorithms.com/algebra/phi-function.html#application cp-algorithms.com — Euler&#039;s totient function application in Euler&#039;s theorem]&lt;br /&gt;
* [http://algorithmica.org/tg/number-theory algorithmica.org — Теория чисел]&lt;br /&gt;
* [http://codeforces.com/blog/entry/43225 Codeforces — Cool tricks using Matrix Exponential]&lt;br /&gt;
* [http://habr.com/ru/post/148901 Habr — Используем быстрое возведение матриц в степень для написания очень быстрого интерпретатора простого языка программирования]&lt;br /&gt;
* [http://habr.com/ru/post/236689 Habr — Автоматическая оптимизация алгоритмов с помощью быстрого возведения матриц в степень]&lt;br /&gt;
&lt;br /&gt;
[[Category:Арифметические алгоритмы]]&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=%D0%9A%D0%B0%D1%82%D0%B5%D0%B3%D0%BE%D1%80%D0%B8%D1%8F:%D0%A3%D1%87%D0%B5%D0%B1%D0%BD%D1%8B%D0%B9_%D0%BA%D1%83%D1%80%D1%81_%C2%AB%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D1%8B_%D0%B8_%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D1%8B_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85%C2%BB&amp;diff=2923</id>
		<title>Категория:Учебный курс «Алгоритмы и структуры данных»</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=%D0%9A%D0%B0%D1%82%D0%B5%D0%B3%D0%BE%D1%80%D0%B8%D1%8F:%D0%A3%D1%87%D0%B5%D0%B1%D0%BD%D1%8B%D0%B9_%D0%BA%D1%83%D1%80%D1%81_%C2%AB%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D1%8B_%D0%B8_%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D1%8B_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85%C2%BB&amp;diff=2923"/>
		<updated>2025-01-05T21:02:41Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&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;
:* [[Поразрядная сортировка]]&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;
:* [[Множество. Реализация на битовых векторах]]&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;
:* 📄 [[Sparse table]]&lt;br /&gt;
:* Sqrt-декомпозиция&lt;br /&gt;
:* [[Алгоритм Мо]]&lt;br /&gt;
|width=450px|&lt;br /&gt;
;Алгоритмы для работы с графами&lt;br /&gt;
:&lt;br /&gt;
:* [[Основные определения. Представление графов]]&lt;br /&gt;
: Поиск в глубину и его приложения&lt;br /&gt;
:* 📄 [[Поиск в глубину]]&amp;lt;sup&amp;gt;&#039;&#039;O(V+E)&#039;&#039;&amp;lt;/sup&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;
:* 📄 [[Поиск в ширину]]&amp;lt;sup&amp;gt;&#039;&#039;O(V+E)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* 📝 [[Алгоритм Дейкстры]]&amp;lt;sup&amp;gt;&#039;&#039;O(V&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;+E) или O(ElogV)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* 📄 [[Алгоритм A*]]&lt;br /&gt;
:* [[Алгоритм Форда-Беллмана]]&amp;lt;sup&amp;gt;&#039;&#039;O(VE)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* [[Кратчайшие пути в ациклических орграфах]]&amp;lt;sup&amp;gt;&#039;&#039;O(V+E)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
: Кратчайшие пути между всеми парами вершин&lt;br /&gt;
:* 📄 [[Алгоритм Флойда]]&amp;lt;sup&amp;gt;&#039;&#039;O(V&amp;lt;sup&amp;gt;3&amp;lt;/sup&amp;gt;)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* [[Алгоритм Джонсона]]&amp;lt;sup&amp;gt;&#039;&#039;O(VElogV)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
: Минимальное остовное дерево&lt;br /&gt;
:* 📝 [[Алгоритм Краскала]]&amp;lt;sup&amp;gt;&#039;&#039;O(ElogV)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* 📄 [[Алгоритм Прима]]&amp;lt;sup&amp;gt;&#039;&#039;O(V&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;+E) или O(ElogV)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
: Максимальный поток&lt;br /&gt;
:* 📄 [[Алгоритм Форда-Фалкерсона]]&amp;lt;sup&amp;gt;&#039;&#039;O(Flow&amp;amp;times;E)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* 📄 [[Алгоритм Эдмондса-Карпа]]&amp;lt;sup&amp;gt;&#039;&#039;O(VE&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* 📄 [[Алгоритм Диница]]&amp;lt;sup&amp;gt;&#039;&#039;O(V&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;E)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* 📝 [[Максимальный поток минимальной стоимости]]&lt;br /&gt;
:* [[Применения максимального потока]]&lt;br /&gt;
: Максимальное паросочетание&lt;br /&gt;
:* 📄 [[Алгоритм Куна]]&amp;lt;sup&amp;gt;&#039;&#039;O(VE)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* 📄 [[Минимальное вершинное покрытие, максимальное независимое множество]]&lt;br /&gt;
: Наименьший общий предок&lt;br /&gt;
:* 📄 [[Метод двоичного подъёма]]&amp;lt;sup&amp;gt;&#039;&#039;O(NlogN), O(logN)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* [[Сведение LCA к RMQ и RMQ к LCA]]&lt;br /&gt;
:* [[Алгоритм Тарьяна (offline)]]&amp;lt;sup&amp;gt;&#039;&#039;O(N), O(1)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
: Декомпозиции деревьев&lt;br /&gt;
:* 📝 [[Heavy-light-декомпозиция]]&lt;br /&gt;
|&lt;br /&gt;
;Полный перебор и методы его оптимизации&lt;br /&gt;
:&lt;br /&gt;
:* [[Полный перебор]]&lt;br /&gt;
:* [[Два указателя]]&lt;br /&gt;
:* Meet in the middle&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;
:* 📄 [[Метод Гаусса]]&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;
:* 📄 [[Хеширование строк]]&lt;br /&gt;
:* 📄 [[Префикс-функция]]&lt;br /&gt;
:* 📄 [[Алгоритм Ахо-Корасик]]&lt;br /&gt;
:* 📄 [[Z-функция]]&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;
&#039;&#039;&amp;amp;copy; В. А. Фолунин, 2012–2025&#039;&#039;&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=%D0%A5%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D1%81%D1%82%D1%80%D0%BE%D0%BA&amp;diff=2922</id>
		<title>Хеширование строк</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=%D0%A5%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D1%81%D1%82%D1%80%D0%BE%D0%BA&amp;diff=2922"/>
		<updated>2024-12-16T15:33:33Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;h&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt; = s&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt;x&amp;lt;sup&amp;gt;n - 1&amp;lt;/sup&amp;gt; + s&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt;x&amp;lt;sup&amp;gt;n - 2&amp;lt;/sup&amp;gt; + ... + s&amp;lt;sub&amp;gt;n - 2&amp;lt;/sub&amp;gt;x + s&amp;lt;sub&amp;gt;n - 1&amp;lt;/sub&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 struct Hasher {&lt;br /&gt;
     long long x, mod;&lt;br /&gt;
     vector&amp;lt;long long&amp;gt; p, h;&lt;br /&gt;
 &lt;br /&gt;
     Hasher(const string &amp;amp;s, long long x = 31, long long mod = 1e9 + 7) : x(x), mod(mod) {&lt;br /&gt;
         p.resize(s.size());&lt;br /&gt;
         h.resize(s.size());&lt;br /&gt;
 &lt;br /&gt;
         p[0] = 1;&lt;br /&gt;
         h[0] = s[0] - &#039;a&#039; + 1;&lt;br /&gt;
 &lt;br /&gt;
         for (int i = 1; i &amp;lt; s.size(); i++) {&lt;br /&gt;
             p[i] = p[i - 1] * x % mod;&lt;br /&gt;
             h[i] = (h[i - 1] * x % mod + s[i] - &#039;a&#039; + 1) % mod;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     long long getHash(int l, int r) {&lt;br /&gt;
         long long res = h[r];&lt;br /&gt;
         if (l)&lt;br /&gt;
             res = (res - p[r - l + 1] * h[l - 1] % mod + mod) % mod;&lt;br /&gt;
         return res;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
 struct DoubleHasher { &lt;br /&gt;
     Hasher h1, h2;&lt;br /&gt;
 &lt;br /&gt;
     DoubleHasher(const string &amp;amp;s) : h1(s), h2(s, 29, 1e9 + 9) {}&lt;br /&gt;
 &lt;br /&gt;
     pair&amp;lt;long long, long long&amp;gt; getHash(int l, int r) {&lt;br /&gt;
         return { h1.getHash(l, r), h2.getHash(l, r) };&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
{| width=&amp;quot;100%&amp;quot;&lt;br /&gt;
| width=50% |&lt;br /&gt;
h&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt; = s&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; + s&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt;x + s&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt;x&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt; + ... + s&amp;lt;sub&amp;gt;n - 1&amp;lt;/sub&amp;gt;x&amp;lt;sup&amp;gt;n - 1&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для выравнивания хеши различных подстрок домножаются на x&amp;lt;sup&amp;gt;n - 1 - L&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 struct Hasher {&lt;br /&gt;
     long long x = 31, mod = 1e9 + 7;&lt;br /&gt;
     vector&amp;lt;long long&amp;gt; p, h;&lt;br /&gt;
 &lt;br /&gt;
     Hasher(const string &amp;amp;s) {&lt;br /&gt;
         p.resize(s.size());&lt;br /&gt;
         h.resize(s.size());&lt;br /&gt;
 &lt;br /&gt;
         p[0] = 1;&lt;br /&gt;
         h[0] = s[0] - &#039;a&#039; + 1;&lt;br /&gt;
 &lt;br /&gt;
         for (int i = 1; i &amp;lt; s.size(); i++) {&lt;br /&gt;
             p[i] = p[i - 1] * x % mod;&lt;br /&gt;
             h[i] = (h[i - 1] + p[i] * (s[i] - &#039;a&#039; + 1) % mod) % mod;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     long long getHash(int l, int r) {&lt;br /&gt;
         long long res = h[r];&lt;br /&gt;
         if (l)&lt;br /&gt;
             res = (res - h[l - 1] + mod) % mod;&lt;br /&gt;
         res = res * p[p.size() - 1 - l] % mod;&lt;br /&gt;
         return res;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
Этот способ непригоден для сравнения участков двух строк разной длины. В данном случае хеш более левого участка следует домножать на x&amp;lt;sup&amp;gt;|L1 - L2|&amp;lt;/sup&amp;gt;.&lt;br /&gt;
 &lt;br /&gt;
| width=&amp;quot;10px&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| width=50% |&lt;br /&gt;
h&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt; = s&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; + s&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt;x + s&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt;x&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt; + ... + s&amp;lt;sub&amp;gt;n - 1&amp;lt;/sub&amp;gt;x&amp;lt;sup&amp;gt;n - 1&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для выравнивания хеши различных подстрок домножаются на (x&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;)&amp;lt;sup&amp;gt;L&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 struct Hasher {&lt;br /&gt;
     long long x = 31, {{Changed|1=xi = 129032259,}} mod = 1e9 + 7;&lt;br /&gt;
     vector&amp;lt;long long&amp;gt; p, {{Changed|pi,}} h;&lt;br /&gt;
 &lt;br /&gt;
     Hasher(const string &amp;amp;s) {&lt;br /&gt;
         p.resize(s.size());&lt;br /&gt;
         {{Changed|pi.resize(s.size());}}&lt;br /&gt;
         h.resize(s.size());&lt;br /&gt;
 &lt;br /&gt;
         p[0] = {{Changed|1=pi[0] = }} 1;&lt;br /&gt;
         h[0] = s[0] - &#039;a&#039; + 1;&lt;br /&gt;
 &lt;br /&gt;
         for (int i = 1; i &amp;lt; s.size(); i++) {&lt;br /&gt;
             p[i] = p[i - 1] * x % mod;&lt;br /&gt;
             {{Changed|1=pi[i] = pi[i - 1] * xi % mod;}}&lt;br /&gt;
             h[i] = (h[i - 1] + p[i] * (s[i] - &#039;a&#039; + 1) % mod) % mod;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     long long getHash(int l, int r) {&lt;br /&gt;
         long long res = h[r];&lt;br /&gt;
         if (l) {&lt;br /&gt;
             res = (res - h[l - 1] + mod) % mod;&lt;br /&gt;
             {{Changed|1=res = (res * pi[l]) % mod}};&lt;br /&gt;
         }&lt;br /&gt;
         return res;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
Теория:&lt;br /&gt;
* [http://e-maxx.ru/algo/string_hashes e-maxx.ru &amp;amp;mdash; Алгоритмы хэширования в задачах на строки]&lt;br /&gt;
* [http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8_%D0%B2_%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B5_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%85%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F._%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A0%D0%B0%D0%B1%D0%B8%D0%BD%D0%B0-%D0%9A%D0%B0%D1%80%D0%BF%D0%B0 neerc.ifmo.ru &amp;amp;mdash; Поиск подстроки в строке с использованием хеширования. Алгоритм Рабина-Карпа]&lt;br /&gt;
* [http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BD%D0%B0%D0%B8%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B5%D0%B9_%D0%BE%D0%B1%D1%89%D0%B5%D0%B9_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8_%D0%B4%D0%B2%D1%83%D1%85_%D1%81%D1%82%D1%80%D0%BE%D0%BA_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%85%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F neerc.ifmo.ru &amp;amp;mdash; Поиск наибольшей общей подстроки двух строк с использованием хеширования]&lt;br /&gt;
* [https://ru.algorithmica.org/cs/hashing/polynomial/ ru.algorithmica.org — Полиномиальное хеширование]&lt;br /&gt;
* [http://habrahabr.ru/post/142589/ habrahabr.ru &amp;amp;mdash; Полиномиальные хеши и их применение]&lt;br /&gt;
* [http://codeforces.com/blog/entry/4898 codeforces.com &amp;amp;mdash; Anti-hash test]&lt;br /&gt;
* [http://codeforces.com/blog/entry/60445 codeforces.com — Полиномиальное хеширование + разбор интересных задач]&lt;br /&gt;
* [https://blog.algoprog.ru/hash-no-multiply/ Калинин П. — Про хеширование без домножения]&lt;br /&gt;
* [https://usaco.guide/gold/string-hashing?lang=cpp usaco.guide — String Hashing]&lt;br /&gt;
Код:&lt;br /&gt;
* [https://github.com/indy256/codelibrary/blob/master/cpp/strings/hashing.cpp codelibrary/cpp/strings/hashing.cpp]&lt;br /&gt;
* [https://github.com/ADJA/algos/blob/master/Strings/Hashing.cpp algos/Strings/Hashing.cpp]&lt;br /&gt;
Задачи:&lt;br /&gt;
* [[:Категория: Задачи: Хеширование строк|Задачи: Хеширование строк]]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Строки]]&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=Z-%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D1%8F&amp;diff=2921</id>
		<title>Z-функция</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=Z-%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D1%8F&amp;diff=2921"/>
		<updated>2024-10-25T11:26:18Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Z-функция строки s — массив длин максимальных подстрок, совпадающих с началом всей строки, для каждого индекса i.&lt;br /&gt;
&lt;br /&gt;
 vector&amp;lt;int&amp;gt; zFunction(const string &amp;amp;s) {&lt;br /&gt;
     vector&amp;lt;int&amp;gt; z(s.size());&lt;br /&gt;
 &lt;br /&gt;
     int blockL = 0, blockR = 0;&lt;br /&gt;
     for (int i = 1; i &amp;lt; s.size(); i++) {&lt;br /&gt;
         int blockLen = 0;&lt;br /&gt;
 &lt;br /&gt;
         if (i &amp;lt;= blockR)&lt;br /&gt;
             blockLen = min(z[i - blockL], blockR - i + 1);&lt;br /&gt;
 &lt;br /&gt;
         while (i + blockLen &amp;lt; s.size() &amp;amp;&amp;amp; s[i + blockLen] == s[blockLen])&lt;br /&gt;
             blockLen++;&lt;br /&gt;
 &lt;br /&gt;
         z[i] = blockLen;&lt;br /&gt;
 &lt;br /&gt;
         if (blockR &amp;lt; i + blockLen - 1) {&lt;br /&gt;
             blockL = i;&lt;br /&gt;
             blockR = i + blockLen - 1;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return z;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
* [https://codeforces.com/edu/course/2/lesson/3 Codeforces EDU — Z-функция]&lt;br /&gt;
* [http://e-maxx.ru/algo/z_function E-maxx — Z-функция строки]&lt;br /&gt;
* [http://neerc.ifmo.ru/wiki/index.php?title=Z-%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D1%8F neerc.ifmo.ru/wiki — Z-функция]&lt;br /&gt;
* [https://brestprog.by/topics/zfunction/ Brestprog — Z-функция]&lt;br /&gt;
* [https://algorithmica.org/ru/string-searching Algorithmica — Префикс- и Z-функция]&lt;br /&gt;
&lt;br /&gt;
[[Category: Строки]]&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=%D0%93%D0%B5%D0%BE%D0%BC%D0%B5%D1%82%D1%80%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B5_%D0%BF%D1%80%D0%B8%D0%BC%D0%B8%D1%82%D0%B8%D0%B2%D1%8B&amp;diff=2920</id>
		<title>Геометрические примитивы</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=%D0%93%D0%B5%D0%BE%D0%BC%D0%B5%D1%82%D1%80%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B5_%D0%BF%D1%80%D0%B8%D0%BC%D0%B8%D1%82%D0%B8%D0%B2%D1%8B&amp;diff=2920"/>
		<updated>2024-08-15T23:48:10Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: /* Точная проверка на пересечение отрезков с целыми координатами */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Точка ==&lt;br /&gt;
&lt;br /&gt;
 #include &amp;lt;algorithm&amp;gt;&lt;br /&gt;
 #include &amp;lt;cmath&amp;gt;&lt;br /&gt;
 #include &amp;lt;vector&amp;gt;&lt;br /&gt;
 using namespace std;&lt;br /&gt;
 &lt;br /&gt;
 const double EPS = 1e-9;&lt;br /&gt;
 &lt;br /&gt;
 struct Point {&lt;br /&gt;
     double x, y;&lt;br /&gt;
 &lt;br /&gt;
     Point() {}&lt;br /&gt;
 &lt;br /&gt;
     Point(double x, double y) : x(x), y(y) {}&lt;br /&gt;
 &lt;br /&gt;
     Point(const Point &amp;amp;a, const Point &amp;amp;b) : x(b.x - a.x), y(b.y - a.y) {}&lt;br /&gt;
 &lt;br /&gt;
     bool operator == (const Point &amp;amp;that) const {&lt;br /&gt;
         return abs(x - that.x) &amp;lt; EPS &amp;amp;&amp;amp; abs(y - that.y) &amp;lt; EPS;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     bool operator &amp;lt; (const Point &amp;amp;that) const {&lt;br /&gt;
         if (abs(x - that.x) &amp;gt;= EPS)&lt;br /&gt;
             return x &amp;lt; that.x;&lt;br /&gt;
         return y + EPS &amp;lt; that.y;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     double angle() const {&lt;br /&gt;
         double a = atan2(y, x);&lt;br /&gt;
         if (a &amp;lt; -EPS)&lt;br /&gt;
             a += 2 * acos(-1.0);&lt;br /&gt;
         return a;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     double length() const {&lt;br /&gt;
         return hypot(x, y);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     double distanceTo(const Point &amp;amp;that) const {&lt;br /&gt;
         return hypot(x - that.x, y - that.y);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     Point operator + (const Point &amp;amp;that) const {&lt;br /&gt;
         return Point(x + that.x, y + that.y);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     Point operator - (const Point &amp;amp;that) const {&lt;br /&gt;
         return Point(x - that.x, y - that.y);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     Point operator * (double k) const {&lt;br /&gt;
         return Point(x * k, y * k);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     Point setLength(double newLength) const {&lt;br /&gt;
         double k = newLength / length();&lt;br /&gt;
         return Point(x * k, y * k);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     Point rotate(double angle) {&lt;br /&gt;
         return Point(x * cos(angle) - y * sin(angle), y * cos(angle) + x * sin(angle));&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     double dotProduct(const Point &amp;amp;that) const {&lt;br /&gt;
         return x * that.x + y * that.y;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     double angleTo(const Point &amp;amp;that) const {&lt;br /&gt;
         return acos(max(-1.0, min(1.0, dotProduct(that) / (length() * that.length()))));&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     bool isOrthogonalTo(const Point &amp;amp;that) const {&lt;br /&gt;
         return abs(dotProduct(that)) &amp;lt; EPS;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     Point orthogonalPoint() const {&lt;br /&gt;
         return Point(-y, x);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     double crossProduct(const Point &amp;amp;that) const {&lt;br /&gt;
         return x * that.y - y * that.x;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     bool isCollinearTo(const Point &amp;amp;that) const {&lt;br /&gt;
         return abs(crossProduct(that)) &amp;lt; EPS;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     friend istream &amp;amp;operator &amp;gt;&amp;gt; (istream &amp;amp;in, Point &amp;amp;p) {&lt;br /&gt;
         return in &amp;gt;&amp;gt; p.x &amp;gt;&amp;gt; p.y;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     friend ostream &amp;amp;operator &amp;lt;&amp;lt; (ostream &amp;amp;out, const Point &amp;amp;p) {&lt;br /&gt;
         return out &amp;lt;&amp;lt; p.x &amp;lt;&amp;lt; &amp;quot; &amp;quot; &amp;lt;&amp;lt; p.y;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
== Прямая ==&lt;br /&gt;
&lt;br /&gt;
 struct Line {&lt;br /&gt;
     double a, b, c;&lt;br /&gt;
 &lt;br /&gt;
     Line() {}&lt;br /&gt;
 &lt;br /&gt;
     Line(double a, double b, double c) : a(a), b(b), c(c) {}&lt;br /&gt;
 &lt;br /&gt;
     Line(const Point &amp;amp;p1, const Point &amp;amp;p2) : a(p1.y - p2.y), b(p2.x - p1.x), c(p1.x * p2.y - p2.x * p1.y) {}&lt;br /&gt;
 &lt;br /&gt;
     static Line LineByVector(const Point &amp;amp;p, const Point &amp;amp;v) {&lt;br /&gt;
         return Line(p, p + v);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     static Line LineByNormal(const Point &amp;amp;p, const Point &amp;amp;n) {&lt;br /&gt;
         return LineByVector(p, n.orthogonalPoint());&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     Point normal() const {&lt;br /&gt;
         return Point(a, b);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     Line orthogonalLine(const Point &amp;amp;p) const {&lt;br /&gt;
         return LineByVector(p, normal());&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     Line parallelLine(const Point &amp;amp;p) const {&lt;br /&gt;
         return LineByNormal(p, normal());&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     Line parallelLine(double distance) const {&lt;br /&gt;
         Point p = (abs(a) &amp;lt; EPS ? Point(0, -c / b) : Point(-c / a, 0));&lt;br /&gt;
         Point p1 = p + normal().setLength(distance);&lt;br /&gt;
         return LineByNormal(p1, normal());&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     int side(const Point &amp;amp;p) const {&lt;br /&gt;
         double r = a * p.x + b * p.y + c;&lt;br /&gt;
         if (abs(r) &amp;lt; EPS)&lt;br /&gt;
             return 0;&lt;br /&gt;
         else&lt;br /&gt;
             return r &amp;gt; 0 ? 1 : -1;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     double distanceTo(const Point &amp;amp;p) const {&lt;br /&gt;
         return abs(a * p.x + b * p.y + c) / sqrt(a * a + b * b);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     bool has(const Point &amp;amp;p) const {&lt;br /&gt;
         return distanceTo(p) &amp;lt; EPS;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     double distanceTo(const Line &amp;amp;that) const {&lt;br /&gt;
         if (normal().isCollinearTo(that.normal())) {&lt;br /&gt;
             Point p = (abs(a) &amp;lt; EPS ? Point(0, -c / b) : Point(-c / a, 0));&lt;br /&gt;
             return that.distanceTo(p);&lt;br /&gt;
         } else&lt;br /&gt;
             return 0;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     bool intersectsWith(const Line &amp;amp;that) const {&lt;br /&gt;
         return distanceTo(that) &amp;lt; EPS;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     Point intersection(const Line &amp;amp;that) const {&lt;br /&gt;
         double d = a * that.b - b * that.a;&lt;br /&gt;
         double dx = -c * that.b - b * -that.c;&lt;br /&gt;
         double dy = a * -that.c - -c * that.a;&lt;br /&gt;
         return Point(dx / d, dy / d);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     friend istream &amp;amp;operator &amp;gt;&amp;gt; (istream &amp;amp;in, Line &amp;amp;l) {&lt;br /&gt;
         return in &amp;gt;&amp;gt; l.a &amp;gt;&amp;gt; l.b &amp;gt;&amp;gt; l.c;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     friend ostream &amp;amp;operator &amp;lt;&amp;lt; (ostream &amp;amp;out, const Line &amp;amp;l) {&lt;br /&gt;
         return out &amp;lt;&amp;lt; l.a &amp;lt;&amp;lt; &amp;quot; &amp;quot; &amp;lt;&amp;lt; l.b &amp;lt;&amp;lt; &amp;quot; &amp;quot; &amp;lt;&amp;lt; l.c;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
== Луч ==&lt;br /&gt;
&lt;br /&gt;
 struct Ray {&lt;br /&gt;
     Point p1, p2;&lt;br /&gt;
     double a, b, c;&lt;br /&gt;
 &lt;br /&gt;
     Ray(const Point &amp;amp;p1, const Point &amp;amp;p2) : p1(p1), p2(p2), a(p1.y - p2.y), b(p2.x - p1.x), c(p1.x * p2.y - p2.x * p1.y) {}&lt;br /&gt;
 &lt;br /&gt;
     double distanceTo(const Point &amp;amp;p) const {&lt;br /&gt;
         if (Point(p1, p).dotProduct(Point(p1, p2)) &amp;gt;= -EPS)&lt;br /&gt;
             return abs(a * p.x + b * p.y + c) / sqrt(a * a + b * b);&lt;br /&gt;
         else&lt;br /&gt;
             return p1.distanceTo(p);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     bool has(const Point &amp;amp;p) const {&lt;br /&gt;
         return distanceTo(p) &amp;lt; EPS;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     double distanceTo(const Ray &amp;amp;that) const {&lt;br /&gt;
         Line l(a, b, c), thatL(that.a, that.b, that.c);&lt;br /&gt;
         if (l.intersectsWith(thatL)) {&lt;br /&gt;
             Point p = l.intersection(thatL);&lt;br /&gt;
             if (has(p) &amp;amp;&amp;amp; that.has(p))&lt;br /&gt;
                 return 0;&lt;br /&gt;
         }&lt;br /&gt;
         return min(distanceTo(that.p1), that.distanceTo(p1));&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     bool intersectsWith(const Ray &amp;amp;that) const {&lt;br /&gt;
         return distanceTo(that) &amp;lt; EPS;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
== Отрезок ==&lt;br /&gt;
&lt;br /&gt;
 struct Segment {&lt;br /&gt;
     Point p1, p2;&lt;br /&gt;
     double a, b, c;&lt;br /&gt;
 &lt;br /&gt;
     Segment(const Point &amp;amp;p1, const Point &amp;amp;p2) : p1(p1), p2(p2), a(p1.y - p2.y), b(p2.x - p1.x), c(p1.x * p2.y - p2.x * p1.y) {}&lt;br /&gt;
 &lt;br /&gt;
     double distanceTo(const Point &amp;amp;p) const {&lt;br /&gt;
         if (Point(p1, p).dotProduct(Point(p1, p2)) &amp;gt;= -EPS &amp;amp;&amp;amp; Point(p2, p).dotProduct(Point(p2, p1)) &amp;gt;= -EPS)&lt;br /&gt;
             return abs(a * p.x + b * p.y + c) / sqrt(a * a + b * b);&lt;br /&gt;
         else&lt;br /&gt;
             return min(p1.distanceTo(p), p2.distanceTo(p));&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     bool has(const Point &amp;amp;p) const {&lt;br /&gt;
         return distanceTo(p) &amp;lt; EPS;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     double distanceTo(const Segment &amp;amp;that) const {&lt;br /&gt;
         Line l(a, b, c), thatL(that.a, that.b, that.c);&lt;br /&gt;
         if (l.intersectsWith(thatL)) {&lt;br /&gt;
             Point p = l.intersection(thatL);&lt;br /&gt;
             if (has(p) &amp;amp;&amp;amp; that.has(p))&lt;br /&gt;
                 return 0;&lt;br /&gt;
         }&lt;br /&gt;
         return min(min(distanceTo(that.p1), distanceTo(that.p2)), min(that.distanceTo(p1), that.distanceTo(p2)));&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     bool intersectsWith(const Segment &amp;amp;that) const {&lt;br /&gt;
         return distanceTo(that) &amp;lt; EPS;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
== Многоугольник ==&lt;br /&gt;
&lt;br /&gt;
 struct Polygon {&lt;br /&gt;
     vector&amp;lt;Point&amp;gt; points;&lt;br /&gt;
 &lt;br /&gt;
     void addPoint(const Point &amp;amp;p) {&lt;br /&gt;
         points.push_back(p);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     bool has(const Point &amp;amp;p) const {&lt;br /&gt;
         bool pos = 0, neg = 0;&lt;br /&gt;
         for (int i = 0; i &amp;lt; points.size(); i++) {&lt;br /&gt;
             const Point &amp;amp;a = points[i], &amp;amp;b = points[(i + 1) % points.size()];&lt;br /&gt;
             Point ab(a, b), ap(a, p);&lt;br /&gt;
             double cross = ab.crossProduct(ap);&lt;br /&gt;
             pos |= cross &amp;gt; EPS;&lt;br /&gt;
             neg |= cross &amp;lt; -EPS;&lt;br /&gt;
         }&lt;br /&gt;
         return !pos || !neg;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     bool isConvex() {&lt;br /&gt;
         bool pos = 0, neg = 0;&lt;br /&gt;
         for (int i = 0; i &amp;lt; points.size(); i++) {&lt;br /&gt;
             const Point &amp;amp;a = points[i], &amp;amp;b = points[(i + 1) % points.size()], &amp;amp;c = points[(i + 2) % points.size()];&lt;br /&gt;
             Point ab(a, b), ac(a, c);&lt;br /&gt;
             double cross = ab.crossProduct(ac);&lt;br /&gt;
             pos |= cross &amp;gt; EPS;&lt;br /&gt;
             neg |= cross &amp;lt; -EPS;&lt;br /&gt;
         }&lt;br /&gt;
         return !pos || !neg;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     double perimeter() const {&lt;br /&gt;
         double p = 0;&lt;br /&gt;
         for (int i = 0; i &amp;lt; points.size(); i++)&lt;br /&gt;
             p += points[i].distanceTo(points[(i + 1) % points.size()]);&lt;br /&gt;
         return p;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     double area() const {&lt;br /&gt;
         double s = 0;&lt;br /&gt;
         for (int i = 0; i &amp;lt; points.size(); i++)&lt;br /&gt;
             s += points[i].crossProduct(points[(i + 1) % points.size()]);&lt;br /&gt;
         return abs(s) / 2;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
== Точная проверка на пересечение отрезков с целыми координатами ==&lt;br /&gt;
 struct Point {&lt;br /&gt;
     long long x, y;&lt;br /&gt;
 &lt;br /&gt;
     Point() {}&lt;br /&gt;
 &lt;br /&gt;
     Point(const Point &amp;amp;a, const Point &amp;amp;b) : x(b.x - a.x), y(b.y - a.y) {}&lt;br /&gt;
 &lt;br /&gt;
     long long crossProduct(const Point &amp;amp;that) const {&lt;br /&gt;
         return x * that.y - y * that.x;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     friend istream &amp;amp;operator &amp;gt;&amp;gt; (istream &amp;amp;in, Point &amp;amp;p) {&lt;br /&gt;
         return in &amp;gt;&amp;gt; p.x &amp;gt;&amp;gt; p.y;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 struct Segment {&lt;br /&gt;
     Point p1, p2;&lt;br /&gt;
 &lt;br /&gt;
     Segment(const Point &amp;amp;p1, const Point &amp;amp;p2) : p1(p1), p2(p2) {}&lt;br /&gt;
 &lt;br /&gt;
     bool intersectsWith(const Segment &amp;amp;that) const {&lt;br /&gt;
         long long abx1 = min(p1.x, p2.x), abx2 = max(p1.x, p2.x);&lt;br /&gt;
         long long cdx1 = min(that.p1.x, that.p2.x), cdx2 = max(that.p1.x, that.p2.x);&lt;br /&gt;
         if (max(abx1, cdx1) &amp;gt; min(abx2, cdx2))&lt;br /&gt;
             return 0;&lt;br /&gt;
 &lt;br /&gt;
         long long aby1 = min(p1.y, p2.y), aby2 = max(p1.y, p2.y);&lt;br /&gt;
         long long cdy1 = min(that.p1.y, that.p2.y), cdy2 = max(that.p1.y, that.p2.y);&lt;br /&gt;
         if (max(aby1, cdy1) &amp;gt; min(aby2, cdy2))&lt;br /&gt;
             return 0;&lt;br /&gt;
 &lt;br /&gt;
         Point ab(p1, p2), ac(p1, that.p1), ad(p1, that.p2);&lt;br /&gt;
         long long abc = ab.crossProduct(ac), abd = ab.crossProduct(ad);&lt;br /&gt;
         if (abc &amp;gt; 0 &amp;amp;&amp;amp; abd &amp;gt; 0 || abc &amp;lt; 0 &amp;amp;&amp;amp; abd &amp;lt; 0)&lt;br /&gt;
             return 0;&lt;br /&gt;
 &lt;br /&gt;
         Point cd(that.p1, that.p2), ca(that.p1, p1), cb(that.p1, p2);&lt;br /&gt;
         long long cda = cd.crossProduct(ca), cdb = cd.crossProduct(cb);&lt;br /&gt;
         if (cda &amp;gt; 0 &amp;amp;&amp;amp; cdb &amp;gt; 0 || cda &amp;lt; 0 &amp;amp;&amp;amp; cdb &amp;lt; 0)&lt;br /&gt;
             return 0;&lt;br /&gt;
 &lt;br /&gt;
         return 1;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     friend istream &amp;amp;operator &amp;gt;&amp;gt; (istream &amp;amp;in, Segment &amp;amp;s) {&lt;br /&gt;
         return in &amp;gt;&amp;gt; s.p1 &amp;gt;&amp;gt; s.p2;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
Теория:&lt;br /&gt;
* [http://e-maxx.ru/bookz/files/andreeva.pdf Андреева Е. В., Егоров Ю. Е. Вычислительная геометрия на плоскости / Е. В. Андреева, Ю. Е. Егоров. // Информатика. &amp;amp;mdash; 2002. &amp;amp;mdash; №39, 40, 43, 44]&lt;br /&gt;
* [http://maratona.ic.unicamp.br/MaratonaVerao2017/documents/geom.pdf Ахмедов М. Geometry, stereometry and spherical geometry]&lt;br /&gt;
* [http://e-maxx.ru/algo/segments_intersection_checking E-maxx — Проверка двух отрезков на пересечение]&lt;br /&gt;
Задачи:&lt;br /&gt;
* [http://codeforces.com/gym/100168 Codeforces 100168 &amp;amp;mdash; 2012-2013 Тренировка СПбГУ C #8. Геометрия. База.]&lt;br /&gt;
* [[:Категория:Задачи: Геометрия|Задачи: Геометрия]]&lt;br /&gt;
&lt;br /&gt;
[[Category: Геометрия]]&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=%D0%93%D0%B5%D0%BE%D0%BC%D0%B5%D1%82%D1%80%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B5_%D0%BF%D1%80%D0%B8%D0%BC%D0%B8%D1%82%D0%B8%D0%B2%D1%8B&amp;diff=2919</id>
		<title>Геометрические примитивы</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=%D0%93%D0%B5%D0%BE%D0%BC%D0%B5%D1%82%D1%80%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B5_%D0%BF%D1%80%D0%B8%D0%BC%D0%B8%D1%82%D0%B8%D0%B2%D1%8B&amp;diff=2919"/>
		<updated>2024-08-15T23:47:26Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: /* Точная проверка на пересечение отрезков с целыми координатами */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Точка ==&lt;br /&gt;
&lt;br /&gt;
 #include &amp;lt;algorithm&amp;gt;&lt;br /&gt;
 #include &amp;lt;cmath&amp;gt;&lt;br /&gt;
 #include &amp;lt;vector&amp;gt;&lt;br /&gt;
 using namespace std;&lt;br /&gt;
 &lt;br /&gt;
 const double EPS = 1e-9;&lt;br /&gt;
 &lt;br /&gt;
 struct Point {&lt;br /&gt;
     double x, y;&lt;br /&gt;
 &lt;br /&gt;
     Point() {}&lt;br /&gt;
 &lt;br /&gt;
     Point(double x, double y) : x(x), y(y) {}&lt;br /&gt;
 &lt;br /&gt;
     Point(const Point &amp;amp;a, const Point &amp;amp;b) : x(b.x - a.x), y(b.y - a.y) {}&lt;br /&gt;
 &lt;br /&gt;
     bool operator == (const Point &amp;amp;that) const {&lt;br /&gt;
         return abs(x - that.x) &amp;lt; EPS &amp;amp;&amp;amp; abs(y - that.y) &amp;lt; EPS;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     bool operator &amp;lt; (const Point &amp;amp;that) const {&lt;br /&gt;
         if (abs(x - that.x) &amp;gt;= EPS)&lt;br /&gt;
             return x &amp;lt; that.x;&lt;br /&gt;
         return y + EPS &amp;lt; that.y;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     double angle() const {&lt;br /&gt;
         double a = atan2(y, x);&lt;br /&gt;
         if (a &amp;lt; -EPS)&lt;br /&gt;
             a += 2 * acos(-1.0);&lt;br /&gt;
         return a;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     double length() const {&lt;br /&gt;
         return hypot(x, y);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     double distanceTo(const Point &amp;amp;that) const {&lt;br /&gt;
         return hypot(x - that.x, y - that.y);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     Point operator + (const Point &amp;amp;that) const {&lt;br /&gt;
         return Point(x + that.x, y + that.y);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     Point operator - (const Point &amp;amp;that) const {&lt;br /&gt;
         return Point(x - that.x, y - that.y);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     Point operator * (double k) const {&lt;br /&gt;
         return Point(x * k, y * k);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     Point setLength(double newLength) const {&lt;br /&gt;
         double k = newLength / length();&lt;br /&gt;
         return Point(x * k, y * k);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     Point rotate(double angle) {&lt;br /&gt;
         return Point(x * cos(angle) - y * sin(angle), y * cos(angle) + x * sin(angle));&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     double dotProduct(const Point &amp;amp;that) const {&lt;br /&gt;
         return x * that.x + y * that.y;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     double angleTo(const Point &amp;amp;that) const {&lt;br /&gt;
         return acos(max(-1.0, min(1.0, dotProduct(that) / (length() * that.length()))));&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     bool isOrthogonalTo(const Point &amp;amp;that) const {&lt;br /&gt;
         return abs(dotProduct(that)) &amp;lt; EPS;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     Point orthogonalPoint() const {&lt;br /&gt;
         return Point(-y, x);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     double crossProduct(const Point &amp;amp;that) const {&lt;br /&gt;
         return x * that.y - y * that.x;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     bool isCollinearTo(const Point &amp;amp;that) const {&lt;br /&gt;
         return abs(crossProduct(that)) &amp;lt; EPS;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     friend istream &amp;amp;operator &amp;gt;&amp;gt; (istream &amp;amp;in, Point &amp;amp;p) {&lt;br /&gt;
         return in &amp;gt;&amp;gt; p.x &amp;gt;&amp;gt; p.y;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     friend ostream &amp;amp;operator &amp;lt;&amp;lt; (ostream &amp;amp;out, const Point &amp;amp;p) {&lt;br /&gt;
         return out &amp;lt;&amp;lt; p.x &amp;lt;&amp;lt; &amp;quot; &amp;quot; &amp;lt;&amp;lt; p.y;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
== Прямая ==&lt;br /&gt;
&lt;br /&gt;
 struct Line {&lt;br /&gt;
     double a, b, c;&lt;br /&gt;
 &lt;br /&gt;
     Line() {}&lt;br /&gt;
 &lt;br /&gt;
     Line(double a, double b, double c) : a(a), b(b), c(c) {}&lt;br /&gt;
 &lt;br /&gt;
     Line(const Point &amp;amp;p1, const Point &amp;amp;p2) : a(p1.y - p2.y), b(p2.x - p1.x), c(p1.x * p2.y - p2.x * p1.y) {}&lt;br /&gt;
 &lt;br /&gt;
     static Line LineByVector(const Point &amp;amp;p, const Point &amp;amp;v) {&lt;br /&gt;
         return Line(p, p + v);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     static Line LineByNormal(const Point &amp;amp;p, const Point &amp;amp;n) {&lt;br /&gt;
         return LineByVector(p, n.orthogonalPoint());&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     Point normal() const {&lt;br /&gt;
         return Point(a, b);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     Line orthogonalLine(const Point &amp;amp;p) const {&lt;br /&gt;
         return LineByVector(p, normal());&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     Line parallelLine(const Point &amp;amp;p) const {&lt;br /&gt;
         return LineByNormal(p, normal());&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     Line parallelLine(double distance) const {&lt;br /&gt;
         Point p = (abs(a) &amp;lt; EPS ? Point(0, -c / b) : Point(-c / a, 0));&lt;br /&gt;
         Point p1 = p + normal().setLength(distance);&lt;br /&gt;
         return LineByNormal(p1, normal());&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     int side(const Point &amp;amp;p) const {&lt;br /&gt;
         double r = a * p.x + b * p.y + c;&lt;br /&gt;
         if (abs(r) &amp;lt; EPS)&lt;br /&gt;
             return 0;&lt;br /&gt;
         else&lt;br /&gt;
             return r &amp;gt; 0 ? 1 : -1;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     double distanceTo(const Point &amp;amp;p) const {&lt;br /&gt;
         return abs(a * p.x + b * p.y + c) / sqrt(a * a + b * b);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     bool has(const Point &amp;amp;p) const {&lt;br /&gt;
         return distanceTo(p) &amp;lt; EPS;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     double distanceTo(const Line &amp;amp;that) const {&lt;br /&gt;
         if (normal().isCollinearTo(that.normal())) {&lt;br /&gt;
             Point p = (abs(a) &amp;lt; EPS ? Point(0, -c / b) : Point(-c / a, 0));&lt;br /&gt;
             return that.distanceTo(p);&lt;br /&gt;
         } else&lt;br /&gt;
             return 0;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     bool intersectsWith(const Line &amp;amp;that) const {&lt;br /&gt;
         return distanceTo(that) &amp;lt; EPS;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     Point intersection(const Line &amp;amp;that) const {&lt;br /&gt;
         double d = a * that.b - b * that.a;&lt;br /&gt;
         double dx = -c * that.b - b * -that.c;&lt;br /&gt;
         double dy = a * -that.c - -c * that.a;&lt;br /&gt;
         return Point(dx / d, dy / d);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     friend istream &amp;amp;operator &amp;gt;&amp;gt; (istream &amp;amp;in, Line &amp;amp;l) {&lt;br /&gt;
         return in &amp;gt;&amp;gt; l.a &amp;gt;&amp;gt; l.b &amp;gt;&amp;gt; l.c;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     friend ostream &amp;amp;operator &amp;lt;&amp;lt; (ostream &amp;amp;out, const Line &amp;amp;l) {&lt;br /&gt;
         return out &amp;lt;&amp;lt; l.a &amp;lt;&amp;lt; &amp;quot; &amp;quot; &amp;lt;&amp;lt; l.b &amp;lt;&amp;lt; &amp;quot; &amp;quot; &amp;lt;&amp;lt; l.c;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
== Луч ==&lt;br /&gt;
&lt;br /&gt;
 struct Ray {&lt;br /&gt;
     Point p1, p2;&lt;br /&gt;
     double a, b, c;&lt;br /&gt;
 &lt;br /&gt;
     Ray(const Point &amp;amp;p1, const Point &amp;amp;p2) : p1(p1), p2(p2), a(p1.y - p2.y), b(p2.x - p1.x), c(p1.x * p2.y - p2.x * p1.y) {}&lt;br /&gt;
 &lt;br /&gt;
     double distanceTo(const Point &amp;amp;p) const {&lt;br /&gt;
         if (Point(p1, p).dotProduct(Point(p1, p2)) &amp;gt;= -EPS)&lt;br /&gt;
             return abs(a * p.x + b * p.y + c) / sqrt(a * a + b * b);&lt;br /&gt;
         else&lt;br /&gt;
             return p1.distanceTo(p);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     bool has(const Point &amp;amp;p) const {&lt;br /&gt;
         return distanceTo(p) &amp;lt; EPS;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     double distanceTo(const Ray &amp;amp;that) const {&lt;br /&gt;
         Line l(a, b, c), thatL(that.a, that.b, that.c);&lt;br /&gt;
         if (l.intersectsWith(thatL)) {&lt;br /&gt;
             Point p = l.intersection(thatL);&lt;br /&gt;
             if (has(p) &amp;amp;&amp;amp; that.has(p))&lt;br /&gt;
                 return 0;&lt;br /&gt;
         }&lt;br /&gt;
         return min(distanceTo(that.p1), that.distanceTo(p1));&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     bool intersectsWith(const Ray &amp;amp;that) const {&lt;br /&gt;
         return distanceTo(that) &amp;lt; EPS;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
== Отрезок ==&lt;br /&gt;
&lt;br /&gt;
 struct Segment {&lt;br /&gt;
     Point p1, p2;&lt;br /&gt;
     double a, b, c;&lt;br /&gt;
 &lt;br /&gt;
     Segment(const Point &amp;amp;p1, const Point &amp;amp;p2) : p1(p1), p2(p2), a(p1.y - p2.y), b(p2.x - p1.x), c(p1.x * p2.y - p2.x * p1.y) {}&lt;br /&gt;
 &lt;br /&gt;
     double distanceTo(const Point &amp;amp;p) const {&lt;br /&gt;
         if (Point(p1, p).dotProduct(Point(p1, p2)) &amp;gt;= -EPS &amp;amp;&amp;amp; Point(p2, p).dotProduct(Point(p2, p1)) &amp;gt;= -EPS)&lt;br /&gt;
             return abs(a * p.x + b * p.y + c) / sqrt(a * a + b * b);&lt;br /&gt;
         else&lt;br /&gt;
             return min(p1.distanceTo(p), p2.distanceTo(p));&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     bool has(const Point &amp;amp;p) const {&lt;br /&gt;
         return distanceTo(p) &amp;lt; EPS;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     double distanceTo(const Segment &amp;amp;that) const {&lt;br /&gt;
         Line l(a, b, c), thatL(that.a, that.b, that.c);&lt;br /&gt;
         if (l.intersectsWith(thatL)) {&lt;br /&gt;
             Point p = l.intersection(thatL);&lt;br /&gt;
             if (has(p) &amp;amp;&amp;amp; that.has(p))&lt;br /&gt;
                 return 0;&lt;br /&gt;
         }&lt;br /&gt;
         return min(min(distanceTo(that.p1), distanceTo(that.p2)), min(that.distanceTo(p1), that.distanceTo(p2)));&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     bool intersectsWith(const Segment &amp;amp;that) const {&lt;br /&gt;
         return distanceTo(that) &amp;lt; EPS;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
== Многоугольник ==&lt;br /&gt;
&lt;br /&gt;
 struct Polygon {&lt;br /&gt;
     vector&amp;lt;Point&amp;gt; points;&lt;br /&gt;
 &lt;br /&gt;
     void addPoint(const Point &amp;amp;p) {&lt;br /&gt;
         points.push_back(p);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     bool has(const Point &amp;amp;p) const {&lt;br /&gt;
         bool pos = 0, neg = 0;&lt;br /&gt;
         for (int i = 0; i &amp;lt; points.size(); i++) {&lt;br /&gt;
             const Point &amp;amp;a = points[i], &amp;amp;b = points[(i + 1) % points.size()];&lt;br /&gt;
             Point ab(a, b), ap(a, p);&lt;br /&gt;
             double cross = ab.crossProduct(ap);&lt;br /&gt;
             pos |= cross &amp;gt; EPS;&lt;br /&gt;
             neg |= cross &amp;lt; -EPS;&lt;br /&gt;
         }&lt;br /&gt;
         return !pos || !neg;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     bool isConvex() {&lt;br /&gt;
         bool pos = 0, neg = 0;&lt;br /&gt;
         for (int i = 0; i &amp;lt; points.size(); i++) {&lt;br /&gt;
             const Point &amp;amp;a = points[i], &amp;amp;b = points[(i + 1) % points.size()], &amp;amp;c = points[(i + 2) % points.size()];&lt;br /&gt;
             Point ab(a, b), ac(a, c);&lt;br /&gt;
             double cross = ab.crossProduct(ac);&lt;br /&gt;
             pos |= cross &amp;gt; EPS;&lt;br /&gt;
             neg |= cross &amp;lt; -EPS;&lt;br /&gt;
         }&lt;br /&gt;
         return !pos || !neg;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     double perimeter() const {&lt;br /&gt;
         double p = 0;&lt;br /&gt;
         for (int i = 0; i &amp;lt; points.size(); i++)&lt;br /&gt;
             p += points[i].distanceTo(points[(i + 1) % points.size()]);&lt;br /&gt;
         return p;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     double area() const {&lt;br /&gt;
         double s = 0;&lt;br /&gt;
         for (int i = 0; i &amp;lt; points.size(); i++)&lt;br /&gt;
             s += points[i].crossProduct(points[(i + 1) % points.size()]);&lt;br /&gt;
         return abs(s) / 2;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
== Точная проверка на пересечение отрезков с целыми координатами ==&lt;br /&gt;
 struct Point {&lt;br /&gt;
     long long x, y;&lt;br /&gt;
 &lt;br /&gt;
     Point() {}&lt;br /&gt;
 &lt;br /&gt;
     Point(const Point &amp;amp;a, const Point &amp;amp;b) : x(b.x - a.x), y(b.y - a.y) {}&lt;br /&gt;
 &lt;br /&gt;
     long long crossProduct(const Point &amp;amp;that) const {&lt;br /&gt;
         return x * that.y - y * that.x;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     friend istream &amp;amp;operator &amp;gt;&amp;gt; (istream &amp;amp;in, Point &amp;amp;p) {&lt;br /&gt;
         return in &amp;gt;&amp;gt; p.x &amp;gt;&amp;gt; p.y;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 struct Segment {&lt;br /&gt;
     Point p1, p2;&lt;br /&gt;
 &lt;br /&gt;
     Segment(const Point &amp;amp;p1, const Point &amp;amp;p2) : p1(p1), p2(p2) {}&lt;br /&gt;
 &lt;br /&gt;
     bool intersectsWith(const Segment &amp;amp;that) const {&lt;br /&gt;
         long long abx1 = min(p1.x, p2.x), abx2 = max(p1.x, p2.x);&lt;br /&gt;
         long long cdx1 = min(that.p1.x, that.p2.x), cdx2 = max(that.p1.x, that.p2.x);&lt;br /&gt;
         if (max(abx1, cdx1) &amp;gt; min(abx2, cdx2))&lt;br /&gt;
             return 0;&lt;br /&gt;
 &lt;br /&gt;
         long long aby1 = min(p1.y, p2.y), aby2 = max(p1.y, p2.y);&lt;br /&gt;
         long long cdy1 = min(that.p1.y, that.p2.y), cdy2 = max(that.p1.y, that.p2.y);&lt;br /&gt;
         if (max(aby1, cdy1) &amp;gt; min(aby2, cdy2))&lt;br /&gt;
             return 0;&lt;br /&gt;
 &lt;br /&gt;
         Point ab(p1, p2), ac(p1, that.p1), ad(p1, that.p2);&lt;br /&gt;
         long long abc = ab.crossProduct(ac), abd = ab.crossProduct(ad);&lt;br /&gt;
         if (abc &amp;gt; 0 &amp;amp;&amp;amp; abd &amp;gt; 0 || abc &amp;lt; 0 &amp;amp;&amp;amp; abd &amp;lt; 0)&lt;br /&gt;
             return 0;&lt;br /&gt;
 &lt;br /&gt;
         Point cd(that.p1, that.p2), ca(that.p1, p1), cb(that.p1, p2);&lt;br /&gt;
         long long cda = cd.crossProduct(ca), cdb = cd.crossProduct(cb);&lt;br /&gt;
         if (cda &amp;gt; 0 &amp;amp;&amp;amp; cdb &amp;gt; 0 || cda &amp;lt; 0 &amp;amp;&amp;amp; cdb &amp;lt; 0)&lt;br /&gt;
             return 0;&lt;br /&gt;
 &lt;br /&gt;
         return 1;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     friend istream &amp;amp;operator &amp;gt;&amp;gt; (istream &amp;amp;in, Segment &amp;amp;s) {&lt;br /&gt;
         return in &amp;gt;&amp;gt; s.p1 &amp;gt;&amp;gt; s.p2;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
Теория:&lt;br /&gt;
* [http://e-maxx.ru/bookz/files/andreeva.pdf Андреева Е. В., Егоров Ю. Е. Вычислительная геометрия на плоскости / Е. В. Андреева, Ю. Е. Егоров. // Информатика. &amp;amp;mdash; 2002. &amp;amp;mdash; №39, 40, 43, 44]&lt;br /&gt;
* [http://maratona.ic.unicamp.br/MaratonaVerao2017/documents/geom.pdf Ахмедов М. Geometry, stereometry and spherical geometry]&lt;br /&gt;
* [http://e-maxx.ru/algo/segments_intersection_checking E-maxx — Проверка двух отрезков на пересечение]&lt;br /&gt;
Задачи:&lt;br /&gt;
* [http://codeforces.com/gym/100168 Codeforces 100168 &amp;amp;mdash; 2012-2013 Тренировка СПбГУ C #8. Геометрия. База.]&lt;br /&gt;
* [[:Категория:Задачи: Геометрия|Задачи: Геометрия]]&lt;br /&gt;
&lt;br /&gt;
[[Category: Геометрия]]&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=%D0%9F%D1%80%D0%BE%D1%81%D1%82%D1%8B%D0%B5_%D1%87%D0%B8%D1%81%D0%BB%D0%B0._%D0%A0%D0%B5%D1%88%D0%B5%D1%82%D0%BE_%D0%AD%D1%80%D0%B0%D1%82%D0%BE%D1%81%D1%84%D0%B5%D0%BD%D0%B0&amp;diff=2918</id>
		<title>Простые числа. Решето Эратосфена</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=%D0%9F%D1%80%D0%BE%D1%81%D1%82%D1%8B%D0%B5_%D1%87%D0%B8%D1%81%D0%BB%D0%B0._%D0%A0%D0%B5%D1%88%D0%B5%D1%82%D0%BE_%D0%AD%D1%80%D0%B0%D1%82%D0%BE%D1%81%D1%84%D0%B5%D0%BD%D0%B0&amp;diff=2918"/>
		<updated>2024-08-14T22:37:47Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;===Проверка числа на простоту, O(sqrt(N))===&lt;br /&gt;
{| width=&amp;quot;100%&amp;quot;&lt;br /&gt;
| width=50% |&lt;br /&gt;
 bool isPrime(int n) {&lt;br /&gt;
     if (n &amp;lt; 2)&lt;br /&gt;
         return 0;&lt;br /&gt;
     &lt;br /&gt;
     for (long long d = 2; d * d &amp;lt;= n; d++)&lt;br /&gt;
         if (n % d == 0)&lt;br /&gt;
             return 0;&lt;br /&gt;
     &lt;br /&gt;
     return 1;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
| width=&amp;quot;10px&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| width=50% |&lt;br /&gt;
 bool isPrime(int n) {&lt;br /&gt;
     if (n &amp;lt; 2)&lt;br /&gt;
         return 0;&lt;br /&gt;
 &lt;br /&gt;
     static vector&amp;lt;int&amp;gt; primes = getPrimes(1e5);&lt;br /&gt;
     for (int i = 0; 1LL * primes[i] * primes[i] &amp;lt;= n; i++)&lt;br /&gt;
         if (n % primes[i] == 0)&lt;br /&gt;
             return 0;&lt;br /&gt;
 &lt;br /&gt;
     return 1;&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Получение списка делителей числа, O(sqrt(N))===&lt;br /&gt;
{| width=&amp;quot;100%&amp;quot;&lt;br /&gt;
| width=50% |&lt;br /&gt;
 vector&amp;lt;int&amp;gt; getDivisors(int n) {&lt;br /&gt;
     vector&amp;lt;int&amp;gt; divisors;&lt;br /&gt;
     &lt;br /&gt;
     for (long long d = 1; d * d &amp;lt;= n; d++) {&lt;br /&gt;
         if (n % d == 0) {&lt;br /&gt;
             divisors.push_back(d);&lt;br /&gt;
             if (d * d != n)&lt;br /&gt;
                 divisors.push_back(n / d);&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
     &lt;br /&gt;
     sort(divisors.begin(), divisors.end());&lt;br /&gt;
     return divisors;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
| width=&amp;quot;10px&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| width=50% |&lt;br /&gt;
 vector&amp;lt;int&amp;gt; getPrimeDivisors(int n) {&lt;br /&gt;
     vector&amp;lt;int&amp;gt; primeDivisors;&lt;br /&gt;
     &lt;br /&gt;
     for (long long d = 2; d * d &amp;lt;= n; d++) {&lt;br /&gt;
         if (n % d == 0) {&lt;br /&gt;
             primeDivisors.push_back(d);&lt;br /&gt;
             while (n % d == 0)&lt;br /&gt;
                 n /= d;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
     if (n != 1)&lt;br /&gt;
         primeDivisors.push_back(n);&lt;br /&gt;
 &lt;br /&gt;
     return primeDivisors;&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Факторизация числа (получение списка простых делителей), O(sqrt(N))===&lt;br /&gt;
{| width=&amp;quot;100%&amp;quot;&lt;br /&gt;
| width=50% |&lt;br /&gt;
 vector&amp;lt;int&amp;gt; factorize(int n) {&lt;br /&gt;
     vector&amp;lt;int&amp;gt; factorization;&lt;br /&gt;
     &lt;br /&gt;
     for (long long d = 2; d * d &amp;lt;= n; d++) {&lt;br /&gt;
         while (n % d == 0) {&lt;br /&gt;
             factorization.push_back(d);&lt;br /&gt;
             n /= d;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
     if (n != 1)&lt;br /&gt;
        factorization.push_back(n);&lt;br /&gt;
     &lt;br /&gt;
     return factorization;&lt;br /&gt;
 }&lt;br /&gt;
| width=&amp;quot;10px&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| width=50% |&lt;br /&gt;
 map&amp;lt;int, int&amp;gt; factorize(int n) {&lt;br /&gt;
     map&amp;lt;int, int&amp;gt; factorization;&lt;br /&gt;
 &lt;br /&gt;
     for (long long d = 2; d * d &amp;lt;= n; d++) {&lt;br /&gt;
         while (n % d == 0) {&lt;br /&gt;
             factorization[d]++;&lt;br /&gt;
             n /= d;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
     if (n != 1)&lt;br /&gt;
        factorization[n]++;&lt;br /&gt;
 &lt;br /&gt;
     return factorization;&lt;br /&gt;
 }&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Решето Эратосфена, O(NloglogN)===&lt;br /&gt;
&lt;br /&gt;
 vector&amp;lt;int&amp;gt; getPrimes(int n) {&lt;br /&gt;
     vector&amp;lt;int&amp;gt; isPrime(n + 1, 1), primes;&lt;br /&gt;
 &lt;br /&gt;
     for (int i = 2; i &amp;lt; isPrime.size(); i++) {&lt;br /&gt;
         if (isPrime[i]) {&lt;br /&gt;
             primes.push_back(i);&lt;br /&gt;
             for (long long j = 1LL * i * i; j &amp;lt; isPrime.size(); j += i)&lt;br /&gt;
                 isPrime[j] = 0;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return primes;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Решето Эратосфена, O(N)===&lt;br /&gt;
 vector&amp;lt;int&amp;gt; getPrimes(int n) {&lt;br /&gt;
     vector&amp;lt;int&amp;gt; minDivisor(n + 1), primes;&lt;br /&gt;
 &lt;br /&gt;
     for (int i = 2; i &amp;lt; minDivisor.size(); i++) {&lt;br /&gt;
         if (!minDivisor[i]) {&lt;br /&gt;
             minDivisor[i] = i;&lt;br /&gt;
             primes.push_back(i);&lt;br /&gt;
         }&lt;br /&gt;
         for (int j = 0; j &amp;lt; primes.size() &amp;amp;&amp;amp; primes[j] &amp;lt;= minDivisor[i] &amp;amp;&amp;amp; 1LL * i * primes[j] &amp;lt; minDivisor.size(); j++)&lt;br /&gt;
             minDivisor[i * primes[j]] = primes[j];&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return primes;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
Теория:&lt;br /&gt;
:* [http://e-maxx.ru/algo/eratosthenes_sieve E-Maxx — Решето Эратосфена]&lt;br /&gt;
:* [http://e-maxx.ru/algo/prime_sieve_linear E-Maxx — Решето Эратосфена с линейным временем работы]&lt;br /&gt;
:* [http://brestprog.by/topics/factorization Brestprog &amp;amp;mdash; Разложение числа на простые множители (факторизация). Делители числа]&lt;br /&gt;
:* [http://brestprog.by/topics/primesieve Brestprog &amp;amp;mdash; Решето Эратосфена]&lt;br /&gt;
:* [http://algorithmica.org/tg/number-theory algorithmica.org — Теория чисел]&lt;br /&gt;
:* [http://algorithmica.org/ru/eratosthenes algorithmica.org — Решето Эратосфена]&lt;br /&gt;
:* [https://usaco.guide/gold/divis?lang=cpp#prime-factorization USACO Guide — Prime Factorization]&lt;br /&gt;
Код:&lt;br /&gt;
:* [https://github.com/indy256/codelibrary/blob/master/cpp/numbertheory/primes_and_divisors.cpp indy256/codelibrary/cpp/numbertheory/primes_and_divisors.cpp]&lt;br /&gt;
:* [https://github.com/indy256/codelibrary/blob/master/cpp/numbertheory/factorization.cpp indy256/codelibrary/cpp/numbertheory/factorization.cpp]&lt;br /&gt;
Задачи:&lt;br /&gt;
:* [http://acmp.ru/?main=task&amp;amp;id_task=200 ACMP #200]&lt;br /&gt;
:* [[:Категория: Задачи: Простые числа|Задачи: Простые числа]]&lt;br /&gt;
:* [http://informatics.mccme.ru/course/view.php?id=17 informatics.mccme.ru &amp;amp;mdash; Курс &amp;amp;laquo;Арифметика и числовые алгоритмы&amp;amp;raquo; &amp;amp;mdash; часть 1]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Арифметические алгоритмы]]&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=%D0%94%D0%BB%D0%B8%D0%BD%D0%BD%D0%B0%D1%8F_%D0%B0%D1%80%D0%B8%D1%84%D0%BC%D0%B5%D1%82%D0%B8%D0%BA%D0%B0&amp;diff=2917</id>
		<title>Длинная арифметика</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=%D0%94%D0%BB%D0%B8%D0%BD%D0%BD%D0%B0%D1%8F_%D0%B0%D1%80%D0%B8%D1%84%D0%BC%D0%B5%D1%82%D0%B8%D0%BA%D0%B0&amp;diff=2917"/>
		<updated>2024-08-13T23:40:05Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: /* Длинная арифметика на vector */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Хранение, ввод и вывод ==&lt;br /&gt;
&lt;br /&gt;
 class BigInteger {&lt;br /&gt;
     static const int BASE = 1e9;&lt;br /&gt;
     static const int BASE_LEN = 9;&lt;br /&gt;
     static const int MAX_SIZE = 1e4;&lt;br /&gt;
     int d[MAX_SIZE];&lt;br /&gt;
     int size;&lt;br /&gt;
 public:&lt;br /&gt;
     BigInteger(long long val = 0) {&lt;br /&gt;
         for (int i = 0; i &amp;lt; MAX_SIZE; i++)&lt;br /&gt;
             d[i] = 0;&lt;br /&gt;
         size = 0;&lt;br /&gt;
         if (val == 0)&lt;br /&gt;
             d[size++] = 0;&lt;br /&gt;
         while (val &amp;gt; 0) {&lt;br /&gt;
             d[size++] = val % BASE;&lt;br /&gt;
             val /= BASE;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
     BigInteger(char val[]) {&lt;br /&gt;
         for (int i = 0; i &amp;lt; MAX_SIZE; i++)&lt;br /&gt;
             d[i] = 0;&lt;br /&gt;
         size = 0;&lt;br /&gt;
         for (int i = strlen(val) - 1; i &amp;gt;= 0; i -= BASE_LEN) {&lt;br /&gt;
             int digit = 0;&lt;br /&gt;
             for (int j = max(0, i - BASE_LEN + 1); j &amp;lt;= i; j++)&lt;br /&gt;
                 digit = digit * 10 + val[j] - &#039;0&#039;;&lt;br /&gt;
             d[size++] = digit;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
     void print() const {&lt;br /&gt;
         static char spec[] = &amp;quot;%00d&amp;quot;;&lt;br /&gt;
         spec[2] = BASE_LEN + &#039;0&#039;;&lt;br /&gt;
         printf(&amp;quot;%d&amp;quot;, d[size - 1]);&lt;br /&gt;
         for (int i = size - 2; i &amp;gt;= 0; i--)&lt;br /&gt;
             printf(spec, d[i]);&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
== Сравнение ==&lt;br /&gt;
&lt;br /&gt;
 int cmp(const BigInteger &amp;amp;that) const {&lt;br /&gt;
     if (size != that.size)&lt;br /&gt;
         return size &amp;lt; that.size ? -1 : 1;&lt;br /&gt;
     for (int i = size - 1; i &amp;gt;= 0; i--) {&lt;br /&gt;
         if (d[i] != that.d[i])&lt;br /&gt;
             return d[i] &amp;lt; that.d[i] ? -1 : 1;&lt;br /&gt;
     }&lt;br /&gt;
     return 0;&lt;br /&gt;
 } &lt;br /&gt;
 bool operator &amp;lt; (const BigInteger &amp;amp;that) const {&lt;br /&gt;
     return cmp(that) &amp;lt; 0;&lt;br /&gt;
 }&lt;br /&gt;
 bool operator == (const BigInteger &amp;amp;that) const {&lt;br /&gt;
     return cmp(that) == 0;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Сложение ==&lt;br /&gt;
&lt;br /&gt;
 BigInteger operator + (const BigInteger &amp;amp;that) const {&lt;br /&gt;
     BigInteger res;        &lt;br /&gt;
     int carry = 0;&lt;br /&gt;
     for (int i = 0; i &amp;lt; max(size, that.size) || carry != 0; i++) {&lt;br /&gt;
         long long x = 0LL + d[i] + that.d[i] + carry;&lt;br /&gt;
         res.d[i] = x % BASE;&lt;br /&gt;
         carry = x / BASE;&lt;br /&gt;
     }&lt;br /&gt;
     res.size = max(size, that.size) + 1;&lt;br /&gt;
     while (res.size &amp;gt; 1 &amp;amp;&amp;amp; res.d[res.size - 1] == 0)&lt;br /&gt;
         res.size--;&lt;br /&gt;
     return res;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Вычитание ==&lt;br /&gt;
&lt;br /&gt;
Предполагается, что уменьшаемое больше вычитаемого.&lt;br /&gt;
&lt;br /&gt;
 BigInteger operator - (const BigInteger &amp;amp;that) const {&lt;br /&gt;
     BigInteger res;&lt;br /&gt;
     int carry = 0;&lt;br /&gt;
     for (int i = 0; i &amp;lt; size || carry != 0; i++) {&lt;br /&gt;
         long long x = 0LL + d[i] - that.d[i] + carry;&lt;br /&gt;
         res.d[i] = (x + BASE) % BASE;&lt;br /&gt;
         carry = x &amp;lt; 0 ? -1 : 0;&lt;br /&gt;
     }&lt;br /&gt;
     res.size = size;&lt;br /&gt;
     while (res.size &amp;gt; 1 &amp;amp;&amp;amp; res.d[res.size - 1] == 0)&lt;br /&gt;
         res.size--;&lt;br /&gt;
     return res;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Умножение ==&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
| width=&amp;quot;50%&amp;quot;  |&lt;br /&gt;
 BigInteger operator * (const BigInteger &amp;amp;that) const {&lt;br /&gt;
     BigInteger res;        &lt;br /&gt;
     for (int i = 0; i &amp;lt; size; i++) {&lt;br /&gt;
         int carry = 0;&lt;br /&gt;
         for (int j = 0; j &amp;lt; that.size || carry != 0; j++) {&lt;br /&gt;
             long long x = res.d[i + j] + 1LL * d[i] * that.d[j] + carry;&lt;br /&gt;
             res.d[i + j] = x % BASE;&lt;br /&gt;
             carry = x / BASE;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
     res.size = size + that.size;&lt;br /&gt;
     while (res.size &amp;gt; 1 &amp;amp;&amp;amp; res.d[res.size - 1] == 0)&lt;br /&gt;
         res.size--;&lt;br /&gt;
     return res;&lt;br /&gt;
 }&lt;br /&gt;
| width=&amp;quot;10px&amp;quot; |&lt;br /&gt;
| width=&amp;quot;50%&amp;quot;  |&lt;br /&gt;
 BigInteger operator * (int that) const {&lt;br /&gt;
     BigInteger res;        &lt;br /&gt;
     int carry = 0;&lt;br /&gt;
     for (int i = 0; i &amp;lt; size || carry != 0; i++) {&lt;br /&gt;
         long long x = 1LL * d[i] * that + carry;&lt;br /&gt;
         res.d[i] = x % BASE;&lt;br /&gt;
         carry = x / BASE;&lt;br /&gt;
     }&lt;br /&gt;
     res.size = size + 1;&lt;br /&gt;
     while (res.size &amp;gt; 1 &amp;amp;&amp;amp; res.d[res.size - 1] == 0)&lt;br /&gt;
         res.size--;&lt;br /&gt;
     return res;&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;
| width=&amp;quot;50%&amp;quot;  |&lt;br /&gt;
 BigInteger operator / (const BigInteger &amp;amp;that) const {&lt;br /&gt;
     BigInteger res, carry;&lt;br /&gt;
     for (int i = size - 1; i &amp;gt;= 0; i--) {&lt;br /&gt;
         carry = carry * BASE;&lt;br /&gt;
         carry.d[0] = d[i];&lt;br /&gt;
         int l = 0, r = BASE - 1, m;&lt;br /&gt;
         while (l + 1 &amp;lt; r) {&lt;br /&gt;
             m = l + (r - l) / 2;&lt;br /&gt;
             if ((that * m).cmp(carry) &amp;lt;= 0)&lt;br /&gt;
                 l = m;&lt;br /&gt;
             else&lt;br /&gt;
                 r = m;&lt;br /&gt;
         }&lt;br /&gt;
         res.d[i] = (that * r).cmp(carry) &amp;lt;= 0 ? r : l;&lt;br /&gt;
         carry = carry - that * res.d[i];&lt;br /&gt;
     }    &lt;br /&gt;
     res.size = size;&lt;br /&gt;
     while (res.size &amp;gt; 1 &amp;amp;&amp;amp; res.d[res.size - 1] == 0)&lt;br /&gt;
         res.size--;&lt;br /&gt;
     return res;&lt;br /&gt;
 }&lt;br /&gt;
| width=&amp;quot;10px&amp;quot; |&lt;br /&gt;
| width=&amp;quot;50%&amp;quot;  |&lt;br /&gt;
 BigInteger operator / (int that) const {&lt;br /&gt;
     BigInteger res;&lt;br /&gt;
     int carry = 0;&lt;br /&gt;
     for (int i = size - 1; i &amp;gt;= 0; i--) {&lt;br /&gt;
         long long x = 1LL * carry * BASE + d[i];&lt;br /&gt;
         res.d[i] = x / that;&lt;br /&gt;
         carry = x % that;&lt;br /&gt;
     }&lt;br /&gt;
     res.size = size;&lt;br /&gt;
     while (res.size &amp;gt; 1 &amp;amp;&amp;amp; res.d[res.size - 1] == 0)&lt;br /&gt;
         res.size--;&lt;br /&gt;
     return res;&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;
| width=&amp;quot;50%&amp;quot;  |&lt;br /&gt;
 BigInteger operator % (const BigInteger &amp;amp;that) const {&lt;br /&gt;
     BigInteger carry;&lt;br /&gt;
     for (int i = size - 1; i &amp;gt;= 0; i--) {&lt;br /&gt;
         carry = carry * BASE;&lt;br /&gt;
         carry.d[0] = d[i];&lt;br /&gt;
         int l = 0, r = BASE - 1, m;&lt;br /&gt;
         while (l + 1 &amp;lt; r) {&lt;br /&gt;
             m = l + (r - l) / 2;&lt;br /&gt;
             if ((that * m).cmp(carry) &amp;lt;= 0)&lt;br /&gt;
                 l = m;&lt;br /&gt;
             else&lt;br /&gt;
                 r = m;&lt;br /&gt;
         }&lt;br /&gt;
         int digit = (that * r).cmp(carry) &amp;lt;= 0 ? r : l;&lt;br /&gt;
         carry = carry - that * digit;&lt;br /&gt;
     }&lt;br /&gt;
     return carry;&lt;br /&gt;
 }&lt;br /&gt;
| width=&amp;quot;10px&amp;quot; |&lt;br /&gt;
| width=&amp;quot;50%&amp;quot;  |&lt;br /&gt;
 int operator % (int that) const {&lt;br /&gt;
     int carry = 0;&lt;br /&gt;
     for (int i = size - 1; i &amp;gt;= 0; i--)&lt;br /&gt;
         carry = (1LL * carry * BASE + d[i]) % that;&lt;br /&gt;
     return carry;&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;
|}&lt;br /&gt;
&lt;br /&gt;
== Длинная арифметика на vector ==&lt;br /&gt;
&lt;br /&gt;
 struct BigInteger {&lt;br /&gt;
     vector&amp;lt;int&amp;gt; digits;&lt;br /&gt;
     inline static const int BASE = 1e9;&lt;br /&gt;
     inline static const int DIGIT_WIDTH = 9;&lt;br /&gt;
 &lt;br /&gt;
     inline int digit(int index) const {&lt;br /&gt;
         return index &amp;lt; digits.size() ? digits[index] : 0;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     inline int &amp;amp;digit(int index) {&lt;br /&gt;
         if (digits.size() &amp;lt;= index)&lt;br /&gt;
             digits.resize(index + 1);&lt;br /&gt;
         return digits[index];&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     inline void removeZeros() {&lt;br /&gt;
         while (!digits.empty() &amp;amp;&amp;amp; !digits.back())&lt;br /&gt;
             digits.pop_back();&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     BigInteger(long long value = 0) {&lt;br /&gt;
         for (; value; value /= BASE)&lt;br /&gt;
             digits.push_back(value % BASE);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     BigInteger(const string &amp;amp;s) {&lt;br /&gt;
         for (int r = s.size() - 1; r &amp;gt;= 0; r -= DIGIT_WIDTH) {&lt;br /&gt;
             int l = max(r - DIGIT_WIDTH + 1, 0);&lt;br /&gt;
             digits.push_back(stoi(s.substr(l, r - l + 1)));&lt;br /&gt;
         }&lt;br /&gt;
         removeZeros();&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     BigInteger operator + (const BigInteger &amp;amp;that) const {&lt;br /&gt;
         BigInteger res;&lt;br /&gt;
         for (int i = 0, carry = 0; i &amp;lt; digits.size() || i &amp;lt; that.digits.size() || carry; i++) {&lt;br /&gt;
             int cur = digit(i) + that.digit(i) + carry;&lt;br /&gt;
             res.digits.push_back(cur % BASE);&lt;br /&gt;
             carry = cur / BASE;&lt;br /&gt;
         }&lt;br /&gt;
         return res;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     BigInteger operator * (const BigInteger &amp;amp;that) const {&lt;br /&gt;
         BigInteger res;&lt;br /&gt;
         for (int i = 0, carry = 0; i &amp;lt; digits.size(); i++) {&lt;br /&gt;
             for (int j = 0; j &amp;lt; that.digits.size() || carry; j++) {&lt;br /&gt;
                 long long cur = res.digit(i + j) + 1LL * digit(i) * that.digit(j) + carry;&lt;br /&gt;
                 res.digit(i + j) = cur % BASE;&lt;br /&gt;
                 carry = cur / BASE;&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         res.removeZeros();&lt;br /&gt;
         return res;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     BigInteger operator / (int that) const {&lt;br /&gt;
         BigInteger res;&lt;br /&gt;
         for (int i = digits.size() - 1, carry = 0; i &amp;gt;= 0; i--) {&lt;br /&gt;
             long long cur = 1LL * carry * BASE + digit(i);&lt;br /&gt;
             res.digit(i) = cur / that;&lt;br /&gt;
             carry = cur % that;&lt;br /&gt;
         }&lt;br /&gt;
         res.removeZeros();&lt;br /&gt;
         return res;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     friend istream &amp;amp;operator &amp;gt;&amp;gt; (istream &amp;amp;in, BigInteger &amp;amp;value) {&lt;br /&gt;
         string s;&lt;br /&gt;
         in &amp;gt;&amp;gt; s;&lt;br /&gt;
         value = BigInteger(s);&lt;br /&gt;
         return in;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     friend ostream &amp;amp;operator &amp;lt;&amp;lt; (ostream &amp;amp;out, const BigInteger &amp;amp;value) {&lt;br /&gt;
         if (value.digits.empty()) {&lt;br /&gt;
             out &amp;lt;&amp;lt; 0;&lt;br /&gt;
         } else {&lt;br /&gt;
             out &amp;lt;&amp;lt; value.digits.back();&lt;br /&gt;
             for (int i = (int)value.digits.size() - 2; i &amp;gt;= 0; i--) {&lt;br /&gt;
                 out.width(DIGIT_WIDTH);&lt;br /&gt;
                 out.fill(&#039;0&#039;);&lt;br /&gt;
                 out &amp;lt;&amp;lt; value.digits[i];&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         return out;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
== Ссылки на задачи ==&lt;br /&gt;
* [http://acmp.ru/?main=task&amp;amp;id_task=103 ACMP #103 &amp;amp;mdash; Снова A + B]&lt;br /&gt;
* [http://acmp.ru/?main=task&amp;amp;id_task=143 ACMP #143 &amp;amp;mdash; A - B]&lt;br /&gt;
* [http://acmp.ru/?main=task&amp;amp;id_task=144 ACMP #144 &amp;amp;mdash; A * B]&lt;br /&gt;
* [http://acmp.ru/?main=task&amp;amp;id_task=145 ACMP #145 &amp;amp;mdash; A div B]&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
* [[Сложная длинная арифметика]]&lt;br /&gt;
* [http://e-maxx.ru/algo/big_integer e-maxx.ru &amp;amp;mdash; Длинная арифметика]&lt;br /&gt;
* [http://cppalgo.blogspot.ru/2010/05/blog-post.html cppalgo.blogspot.ru &amp;amp;mdash; Длинная арифметика]&lt;br /&gt;
* [http://brestprog.neocities.org/lections/longarithmetics.html brestprog.neocities.org &amp;amp;mdash; Длинная арифметика]&lt;br /&gt;
* [http://informatics.mccme.ru/course/view.php?id=17 informatics.mccme.ru &amp;amp;mdash; Курс &amp;amp;laquo;Арифметика и числовые алгоритмы&amp;amp;raquo; &amp;amp;mdash; часть 5]&lt;br /&gt;
* [http://github.com/ADJA/algos/blob/master/NumberTheory/BigInt.cpp Algos &amp;amp;mdash; Structure implementing long arithmetic in C++]&lt;br /&gt;
&lt;br /&gt;
[[Category:Арифметические алгоритмы]]&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=%D0%94%D0%BB%D0%B8%D0%BD%D0%BD%D0%B0%D1%8F_%D0%B0%D1%80%D0%B8%D1%84%D0%BC%D0%B5%D1%82%D0%B8%D0%BA%D0%B0&amp;diff=2916</id>
		<title>Длинная арифметика</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=%D0%94%D0%BB%D0%B8%D0%BD%D0%BD%D0%B0%D1%8F_%D0%B0%D1%80%D0%B8%D1%84%D0%BC%D0%B5%D1%82%D0%B8%D0%BA%D0%B0&amp;diff=2916"/>
		<updated>2024-07-21T14:16:00Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Хранение, ввод и вывод ==&lt;br /&gt;
&lt;br /&gt;
 class BigInteger {&lt;br /&gt;
     static const int BASE = 1e9;&lt;br /&gt;
     static const int BASE_LEN = 9;&lt;br /&gt;
     static const int MAX_SIZE = 1e4;&lt;br /&gt;
     int d[MAX_SIZE];&lt;br /&gt;
     int size;&lt;br /&gt;
 public:&lt;br /&gt;
     BigInteger(long long val = 0) {&lt;br /&gt;
         for (int i = 0; i &amp;lt; MAX_SIZE; i++)&lt;br /&gt;
             d[i] = 0;&lt;br /&gt;
         size = 0;&lt;br /&gt;
         if (val == 0)&lt;br /&gt;
             d[size++] = 0;&lt;br /&gt;
         while (val &amp;gt; 0) {&lt;br /&gt;
             d[size++] = val % BASE;&lt;br /&gt;
             val /= BASE;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
     BigInteger(char val[]) {&lt;br /&gt;
         for (int i = 0; i &amp;lt; MAX_SIZE; i++)&lt;br /&gt;
             d[i] = 0;&lt;br /&gt;
         size = 0;&lt;br /&gt;
         for (int i = strlen(val) - 1; i &amp;gt;= 0; i -= BASE_LEN) {&lt;br /&gt;
             int digit = 0;&lt;br /&gt;
             for (int j = max(0, i - BASE_LEN + 1); j &amp;lt;= i; j++)&lt;br /&gt;
                 digit = digit * 10 + val[j] - &#039;0&#039;;&lt;br /&gt;
             d[size++] = digit;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
     void print() const {&lt;br /&gt;
         static char spec[] = &amp;quot;%00d&amp;quot;;&lt;br /&gt;
         spec[2] = BASE_LEN + &#039;0&#039;;&lt;br /&gt;
         printf(&amp;quot;%d&amp;quot;, d[size - 1]);&lt;br /&gt;
         for (int i = size - 2; i &amp;gt;= 0; i--)&lt;br /&gt;
             printf(spec, d[i]);&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
== Сравнение ==&lt;br /&gt;
&lt;br /&gt;
 int cmp(const BigInteger &amp;amp;that) const {&lt;br /&gt;
     if (size != that.size)&lt;br /&gt;
         return size &amp;lt; that.size ? -1 : 1;&lt;br /&gt;
     for (int i = size - 1; i &amp;gt;= 0; i--) {&lt;br /&gt;
         if (d[i] != that.d[i])&lt;br /&gt;
             return d[i] &amp;lt; that.d[i] ? -1 : 1;&lt;br /&gt;
     }&lt;br /&gt;
     return 0;&lt;br /&gt;
 } &lt;br /&gt;
 bool operator &amp;lt; (const BigInteger &amp;amp;that) const {&lt;br /&gt;
     return cmp(that) &amp;lt; 0;&lt;br /&gt;
 }&lt;br /&gt;
 bool operator == (const BigInteger &amp;amp;that) const {&lt;br /&gt;
     return cmp(that) == 0;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Сложение ==&lt;br /&gt;
&lt;br /&gt;
 BigInteger operator + (const BigInteger &amp;amp;that) const {&lt;br /&gt;
     BigInteger res;        &lt;br /&gt;
     int carry = 0;&lt;br /&gt;
     for (int i = 0; i &amp;lt; max(size, that.size) || carry != 0; i++) {&lt;br /&gt;
         long long x = 0LL + d[i] + that.d[i] + carry;&lt;br /&gt;
         res.d[i] = x % BASE;&lt;br /&gt;
         carry = x / BASE;&lt;br /&gt;
     }&lt;br /&gt;
     res.size = max(size, that.size) + 1;&lt;br /&gt;
     while (res.size &amp;gt; 1 &amp;amp;&amp;amp; res.d[res.size - 1] == 0)&lt;br /&gt;
         res.size--;&lt;br /&gt;
     return res;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Вычитание ==&lt;br /&gt;
&lt;br /&gt;
Предполагается, что уменьшаемое больше вычитаемого.&lt;br /&gt;
&lt;br /&gt;
 BigInteger operator - (const BigInteger &amp;amp;that) const {&lt;br /&gt;
     BigInteger res;&lt;br /&gt;
     int carry = 0;&lt;br /&gt;
     for (int i = 0; i &amp;lt; size || carry != 0; i++) {&lt;br /&gt;
         long long x = 0LL + d[i] - that.d[i] + carry;&lt;br /&gt;
         res.d[i] = (x + BASE) % BASE;&lt;br /&gt;
         carry = x &amp;lt; 0 ? -1 : 0;&lt;br /&gt;
     }&lt;br /&gt;
     res.size = size;&lt;br /&gt;
     while (res.size &amp;gt; 1 &amp;amp;&amp;amp; res.d[res.size - 1] == 0)&lt;br /&gt;
         res.size--;&lt;br /&gt;
     return res;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Умножение ==&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
| width=&amp;quot;50%&amp;quot;  |&lt;br /&gt;
 BigInteger operator * (const BigInteger &amp;amp;that) const {&lt;br /&gt;
     BigInteger res;        &lt;br /&gt;
     for (int i = 0; i &amp;lt; size; i++) {&lt;br /&gt;
         int carry = 0;&lt;br /&gt;
         for (int j = 0; j &amp;lt; that.size || carry != 0; j++) {&lt;br /&gt;
             long long x = res.d[i + j] + 1LL * d[i] * that.d[j] + carry;&lt;br /&gt;
             res.d[i + j] = x % BASE;&lt;br /&gt;
             carry = x / BASE;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
     res.size = size + that.size;&lt;br /&gt;
     while (res.size &amp;gt; 1 &amp;amp;&amp;amp; res.d[res.size - 1] == 0)&lt;br /&gt;
         res.size--;&lt;br /&gt;
     return res;&lt;br /&gt;
 }&lt;br /&gt;
| width=&amp;quot;10px&amp;quot; |&lt;br /&gt;
| width=&amp;quot;50%&amp;quot;  |&lt;br /&gt;
 BigInteger operator * (int that) const {&lt;br /&gt;
     BigInteger res;        &lt;br /&gt;
     int carry = 0;&lt;br /&gt;
     for (int i = 0; i &amp;lt; size || carry != 0; i++) {&lt;br /&gt;
         long long x = 1LL * d[i] * that + carry;&lt;br /&gt;
         res.d[i] = x % BASE;&lt;br /&gt;
         carry = x / BASE;&lt;br /&gt;
     }&lt;br /&gt;
     res.size = size + 1;&lt;br /&gt;
     while (res.size &amp;gt; 1 &amp;amp;&amp;amp; res.d[res.size - 1] == 0)&lt;br /&gt;
         res.size--;&lt;br /&gt;
     return res;&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;
| width=&amp;quot;50%&amp;quot;  |&lt;br /&gt;
 BigInteger operator / (const BigInteger &amp;amp;that) const {&lt;br /&gt;
     BigInteger res, carry;&lt;br /&gt;
     for (int i = size - 1; i &amp;gt;= 0; i--) {&lt;br /&gt;
         carry = carry * BASE;&lt;br /&gt;
         carry.d[0] = d[i];&lt;br /&gt;
         int l = 0, r = BASE - 1, m;&lt;br /&gt;
         while (l + 1 &amp;lt; r) {&lt;br /&gt;
             m = l + (r - l) / 2;&lt;br /&gt;
             if ((that * m).cmp(carry) &amp;lt;= 0)&lt;br /&gt;
                 l = m;&lt;br /&gt;
             else&lt;br /&gt;
                 r = m;&lt;br /&gt;
         }&lt;br /&gt;
         res.d[i] = (that * r).cmp(carry) &amp;lt;= 0 ? r : l;&lt;br /&gt;
         carry = carry - that * res.d[i];&lt;br /&gt;
     }    &lt;br /&gt;
     res.size = size;&lt;br /&gt;
     while (res.size &amp;gt; 1 &amp;amp;&amp;amp; res.d[res.size - 1] == 0)&lt;br /&gt;
         res.size--;&lt;br /&gt;
     return res;&lt;br /&gt;
 }&lt;br /&gt;
| width=&amp;quot;10px&amp;quot; |&lt;br /&gt;
| width=&amp;quot;50%&amp;quot;  |&lt;br /&gt;
 BigInteger operator / (int that) const {&lt;br /&gt;
     BigInteger res;&lt;br /&gt;
     int carry = 0;&lt;br /&gt;
     for (int i = size - 1; i &amp;gt;= 0; i--) {&lt;br /&gt;
         long long x = 1LL * carry * BASE + d[i];&lt;br /&gt;
         res.d[i] = x / that;&lt;br /&gt;
         carry = x % that;&lt;br /&gt;
     }&lt;br /&gt;
     res.size = size;&lt;br /&gt;
     while (res.size &amp;gt; 1 &amp;amp;&amp;amp; res.d[res.size - 1] == 0)&lt;br /&gt;
         res.size--;&lt;br /&gt;
     return res;&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;
| width=&amp;quot;50%&amp;quot;  |&lt;br /&gt;
 BigInteger operator % (const BigInteger &amp;amp;that) const {&lt;br /&gt;
     BigInteger carry;&lt;br /&gt;
     for (int i = size - 1; i &amp;gt;= 0; i--) {&lt;br /&gt;
         carry = carry * BASE;&lt;br /&gt;
         carry.d[0] = d[i];&lt;br /&gt;
         int l = 0, r = BASE - 1, m;&lt;br /&gt;
         while (l + 1 &amp;lt; r) {&lt;br /&gt;
             m = l + (r - l) / 2;&lt;br /&gt;
             if ((that * m).cmp(carry) &amp;lt;= 0)&lt;br /&gt;
                 l = m;&lt;br /&gt;
             else&lt;br /&gt;
                 r = m;&lt;br /&gt;
         }&lt;br /&gt;
         int digit = (that * r).cmp(carry) &amp;lt;= 0 ? r : l;&lt;br /&gt;
         carry = carry - that * digit;&lt;br /&gt;
     }&lt;br /&gt;
     return carry;&lt;br /&gt;
 }&lt;br /&gt;
| width=&amp;quot;10px&amp;quot; |&lt;br /&gt;
| width=&amp;quot;50%&amp;quot;  |&lt;br /&gt;
 int operator % (int that) const {&lt;br /&gt;
     int carry = 0;&lt;br /&gt;
     for (int i = size - 1; i &amp;gt;= 0; i--)&lt;br /&gt;
         carry = (1LL * carry * BASE + d[i]) % that;&lt;br /&gt;
     return carry;&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;
|}&lt;br /&gt;
&lt;br /&gt;
== Длинная арифметика на vector ==&lt;br /&gt;
&lt;br /&gt;
 struct BigInteger {&lt;br /&gt;
     vector&amp;lt;int&amp;gt; digits;&lt;br /&gt;
 &lt;br /&gt;
     BigInteger(long long value = 0) {&lt;br /&gt;
         for ( ; value; value /= 10)&lt;br /&gt;
             digits.push_back(value % 10);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     BigInteger operator + (const BigInteger &amp;amp;that) const {&lt;br /&gt;
         BigInteger res;&lt;br /&gt;
         for (int i = 0, carry = 0; i &amp;lt; digits.size() || i &amp;lt; that.digits.size() || carry; i++) {&lt;br /&gt;
             int digit = i &amp;lt; digits.size() ? digits[i] : 0;&lt;br /&gt;
             int thatDigit = i &amp;lt; that.digits.size() ? that.digits[i] : 0;&lt;br /&gt;
             int sum = digit + thatDigit + carry;&lt;br /&gt;
             res.digits.push_back(sum % 10);&lt;br /&gt;
             carry = sum / 10;&lt;br /&gt;
         }&lt;br /&gt;
         return res;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     friend ostream &amp;amp;operator &amp;lt;&amp;lt; (ostream &amp;amp;out, const BigInteger &amp;amp;value) {&lt;br /&gt;
         if (value.digits.empty()) {&lt;br /&gt;
             out &amp;lt;&amp;lt; 0;&lt;br /&gt;
         } else {&lt;br /&gt;
             for (int i = value.digits.size() - 1; i &amp;gt;= 0; i--)&lt;br /&gt;
                 out &amp;lt;&amp;lt; value.digits[i];&lt;br /&gt;
         }&lt;br /&gt;
         return out;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
== Ссылки на задачи ==&lt;br /&gt;
* [http://acmp.ru/?main=task&amp;amp;id_task=103 ACMP #103 &amp;amp;mdash; Снова A + B]&lt;br /&gt;
* [http://acmp.ru/?main=task&amp;amp;id_task=143 ACMP #143 &amp;amp;mdash; A - B]&lt;br /&gt;
* [http://acmp.ru/?main=task&amp;amp;id_task=144 ACMP #144 &amp;amp;mdash; A * B]&lt;br /&gt;
* [http://acmp.ru/?main=task&amp;amp;id_task=145 ACMP #145 &amp;amp;mdash; A div B]&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
* [[Сложная длинная арифметика]]&lt;br /&gt;
* [http://e-maxx.ru/algo/big_integer e-maxx.ru &amp;amp;mdash; Длинная арифметика]&lt;br /&gt;
* [http://cppalgo.blogspot.ru/2010/05/blog-post.html cppalgo.blogspot.ru &amp;amp;mdash; Длинная арифметика]&lt;br /&gt;
* [http://brestprog.neocities.org/lections/longarithmetics.html brestprog.neocities.org &amp;amp;mdash; Длинная арифметика]&lt;br /&gt;
* [http://informatics.mccme.ru/course/view.php?id=17 informatics.mccme.ru &amp;amp;mdash; Курс &amp;amp;laquo;Арифметика и числовые алгоритмы&amp;amp;raquo; &amp;amp;mdash; часть 5]&lt;br /&gt;
* [http://github.com/ADJA/algos/blob/master/NumberTheory/BigInt.cpp Algos &amp;amp;mdash; Structure implementing long arithmetic in C++]&lt;br /&gt;
&lt;br /&gt;
[[Category:Арифметические алгоритмы]]&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A4%D0%BE%D1%80%D0%B4%D0%B0-%D0%91%D0%B5%D0%BB%D0%BB%D0%BC%D0%B0%D0%BD%D0%B0&amp;diff=2915</id>
		<title>Алгоритм Форда-Беллмана</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A4%D0%BE%D1%80%D0%B4%D0%B0-%D0%91%D0%B5%D0%BB%D0%BB%D0%BC%D0%B0%D0%BD%D0%B0&amp;diff=2915"/>
		<updated>2024-04-21T13:19:57Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: /* TLDR */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== TLDR ==&lt;br /&gt;
&amp;lt;youtube width=&amp;quot;300&amp;quot; height=&amp;quot;180&amp;quot;&amp;gt;fwaaNf2N4P8&amp;lt;/youtube&amp;gt;&lt;br /&gt;
&amp;lt;youtube width=&amp;quot;300&amp;quot; height=&amp;quot;180&amp;quot;&amp;gt;cE5n2IKf7W4&amp;lt;/youtube&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;youtube width=&amp;quot;300&amp;quot; height=&amp;quot;180&amp;quot;&amp;gt;LVnPNWwd-yo&amp;lt;/youtube&amp;gt;&lt;br /&gt;
&amp;lt;youtube width=&amp;quot;300&amp;quot; height=&amp;quot;180&amp;quot;&amp;gt;4njNaLG5p1A&amp;lt;/youtube&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[file:fordbellman.jpg]]&lt;br /&gt;
&lt;br /&gt;
== Ссылки на задачи ==&lt;br /&gt;
* [http://acmp.ru/?main=task&amp;amp;id_task=138 ACMP #138 &amp;amp;mdash; Алгоритм Форда-Беллмана]&lt;br /&gt;
* [http://acmp.ru/?main=task&amp;amp;id_task=139 ACMP #139 &amp;amp;mdash; Лабиринт знаний]&lt;br /&gt;
* [http://acmp.ru/?main=task&amp;amp;id_task=140 ACMP #140 &amp;amp;mdash; Цикл отрицательного веса]&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
* [http://e-maxx.ru/algo/ford_bellman e-maxx.ru &amp;amp;mdash; Алгоритм Форда-Беллмана]&lt;br /&gt;
* [http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A4%D0%BE%D1%80%D0%B4%D0%B0-%D0%91%D0%B5%D0%BB%D0%BB%D0%BC%D0%B0%D0%BD%D0%B0 neerc.ifmo.ru/wiki &amp;amp;mdash; Алгоритм Форда-Беллмана]&lt;br /&gt;
* [http://algorithmica.org/tg/shortest-paths algorithmica.org — Кратчайшие пути в графе]&lt;br /&gt;
* [http://informatics.mccme.ru/course/view.php?id=6 informatics.mccme.ru &amp;amp;mdash; Курс &amp;amp;laquo;Алгоритмы на графах&amp;amp;raquo; &amp;amp;mdash; часть 6]&lt;br /&gt;
* [http://algs4.cs.princeton.edu/lectures/44ShortestPaths.pdf algs4.cs.princeton.edu/lectures &amp;amp;mdash; 4.4 Shortest Paths]&lt;br /&gt;
* [http://brilliant.org/wiki/bellman-ford-algorithm Brilliant.org — Bellman-Ford Algorithm]&lt;br /&gt;
* [http://visualgo.net/sssp.html VisuAlgo &amp;amp;mdash; Single-Source Shortest Paths]&lt;br /&gt;
* [http://github.com/indy256/codelibrary/blob/master/java/src/BellmanFord2.java CodeLibrary &amp;amp;mdash; Bellman–Ford algorithm]&lt;br /&gt;
* [http://github.com/ADJA/algos/blob/master/Graphs/BellmanFord.cpp Algos &amp;amp;mdash; Bellman-Ford algorithm]&lt;br /&gt;
&lt;br /&gt;
[[Category:Кратчайшие пути из одной вершины]]&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A4%D0%BB%D0%BE%D0%B9%D0%B4%D0%B0&amp;diff=2914</id>
		<title>Алгоритм Флойда</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A4%D0%BB%D0%BE%D0%B9%D0%B4%D0%B0&amp;diff=2914"/>
		<updated>2024-04-21T13:19:51Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: /* TLDR */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== TLDR ==&lt;br /&gt;
&amp;lt;youtube width=&amp;quot;300&amp;quot; height=&amp;quot;180&amp;quot;&amp;gt;c7ahlIwBL7o&amp;lt;/youtube&amp;gt;&lt;br /&gt;
&amp;lt;youtube width=&amp;quot;300&amp;quot; height=&amp;quot;180&amp;quot;&amp;gt;8JQ565Rz7d8&amp;lt;/youtube&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;youtube width=&amp;quot;300&amp;quot; height=&amp;quot;180&amp;quot;&amp;gt;LVnPNWwd-yo&amp;lt;/youtube&amp;gt;&lt;br /&gt;
&amp;lt;youtube width=&amp;quot;300&amp;quot; height=&amp;quot;180&amp;quot;&amp;gt;4njNaLG5p1A&amp;lt;/youtube&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Код ==&lt;br /&gt;
&lt;br /&gt;
Ищем расстояния во взвешенном графе от каждой вершины до всех остальных. Веса могут быть любыми.&lt;br /&gt;
&lt;br /&gt;
Идея алгоритма:&lt;br /&gt;
* Инициализация. Пусть dist[a][b] — кратчайшее расстояние от a до b, но длиной не более чем одно ребро. Нетрудно понять, что dist — это просто матрица смежности графа (возможно, с небольшими модификациями):&lt;br /&gt;
:* Случай простого графа: dist[a][a] = 0, dist[a][b] = весу ребра (a, b); если ребра (a, b) нет, то dist[a][b] = INF;&lt;br /&gt;
:* Если есть кратные рёбра, то dist[a][b] = весу минимального ребра между a и b;&lt;br /&gt;
:* Если есть петли, то dist[a][a] = min(0, вес минимальной петли из a);&lt;br /&gt;
* Шаг 0. Теперь разрешаем путям проходить через вершину 0. Обновляем dist: dist[a][b] = min(dist[a][b], dist[a][0] + dist[0][b]);&lt;br /&gt;
* Шаг 1. Теперь разрешаем путям проходить через вершину 1. Обновляем dist: dist[a][b] = min(dist[a][b], dist[a][1] + dist[1][b]);&lt;br /&gt;
* Повторяем до шага vertexCount - 1.&lt;br /&gt;
&lt;br /&gt;
 const int INF = 1e9;&lt;br /&gt;
 vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; dist(vertexCount, vector&amp;lt;int&amp;gt;(vertexCount, INF));&lt;br /&gt;
 &lt;br /&gt;
 for (int v = 0; v &amp;lt; vertexCount; v++)&lt;br /&gt;
     dist[v][v] = 0;&lt;br /&gt;
 &lt;br /&gt;
 for (int i = 0; i &amp;lt; edgeCount; i++) {&lt;br /&gt;
     int a, b, w;&lt;br /&gt;
     cin &amp;gt;&amp;gt; a &amp;gt;&amp;gt; b &amp;gt;&amp;gt; w;&lt;br /&gt;
     dist[a][b] = min(dist[a][b], w);&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 for (int v = 0; v &amp;lt; vertexCount; v++) {&lt;br /&gt;
     for (int a = 0; a &amp;lt; vertexCount; a++) {&lt;br /&gt;
         for (int b = 0; b &amp;lt; vertexCount; b++) {&lt;br /&gt;
             if (dist[a][v] == INF || dist[v][b] == INF)&lt;br /&gt;
                 continue;&lt;br /&gt;
             dist[a][b] = min(dist[a][b], dist[a][v] + dist[v][b]);&lt;br /&gt;
             dist[a][b] = max(dist[a][b], -INF);&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
На что нужно обратить внимание:&lt;br /&gt;
* При инициализации матрицы на главной диагонали должны быть нули (если нет петель отрицательного веса), а там, где рёбер нет — бесконечности;&lt;br /&gt;
* При наличии отрицательных рёбер следует избегать присваиваний вида (INF - 1). Для этого, если хотя бы один из фрагментов пути равен INF, нужно делать continue;&lt;br /&gt;
* При наличии отрицательных циклов расстояния могут очень быстро уменьшаться и приводить к отрицательным переполнениям. Поэтому нужно ограничивать отрицательные числа снизу.&lt;br /&gt;
&lt;br /&gt;
== Отрицательные циклы ==&lt;br /&gt;
&lt;br /&gt;
Вершина v лежит на отрицательном цикле, если после запуска алгоритма Флойда dist[v][v] &amp;lt; 0. Наличие такой вершины — критерий отрицательного цикла в алгоритме Флойда.&lt;br /&gt;
&lt;br /&gt;
При наличии отрицательных циклов между некоторыми парами вершин может не существовать кратчайшего пути. Между вершинами a и b нет кратчайшего пути, если существует вершина v, лежащая на отрицательном цикле, которая достижима из a и из которой достижима b.&lt;br /&gt;
&lt;br /&gt;
 const int NO_SHORTEST_PATH = -2e9;&lt;br /&gt;
 &lt;br /&gt;
 for (int v = 0; v &amp;lt; vertexCount; v++)&lt;br /&gt;
     for (int a = 0; a &amp;lt; vertexCount; a++)&lt;br /&gt;
         for (int b = 0; b &amp;lt; vertexCount; b++)&lt;br /&gt;
             if (dist[a][v] != INF &amp;amp;&amp;amp; dist[v][v] &amp;lt; 0 &amp;amp;&amp;amp; dist[v][b] != INF)&lt;br /&gt;
                 dist[a][b] = NO_SHORTEST_PATH;&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
Теория:&lt;br /&gt;
* [http://e-maxx.ru/algo/floyd_warshall_algorithm e-maxx.ru — Алгоритм Флойда-Уоршелла]&lt;br /&gt;
* [https://cp-algorithms.com/graph/all-pair-shortest-path-floyd-warshall.html cp-algorithms.com — Floyd-Warshall Algorithm]&lt;br /&gt;
* [http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A4%D0%BB%D0%BE%D0%B9%D0%B4%D0%B0 neerc.ifmo.ru/wiki — Алгоритм Флойда]&lt;br /&gt;
* [http://brestprog.by/topics/floyd/ brestprog.by — Алгоритм Флойда-Уоршелла]&lt;br /&gt;
* [http://algorithmica.org/tg/shortest-paths algorithmica.org — Кратчайшие пути в графе]&lt;br /&gt;
* [http://brilliant.org/wiki/floyd-warshall-algorithm brilliant.org — Floyd-Warshall Algorithm]&lt;br /&gt;
* usaco.guide — [https://usaco.guide/gold/shortest-paths?lang=cpp#floyd-warshall Shortest Paths with Non-Negative Edge Weights: Floyd-Warshall], [https://usaco.guide/adv/sp-neg?lang=cpp#floyd-warshall Shortest Paths with Negative Edge Weights: Floyd-Warshall]&lt;br /&gt;
Демонстрация:&lt;br /&gt;
* [https://www.cs.usfca.edu/~galles/visualization/Floyd.html www.cs.usfca.edu — Floyd-Warshall All-Pairs Shortest Path]&lt;br /&gt;
Код:&lt;br /&gt;
* [https://github.com/indy256/codelibrary/blob/master/java/graphs/shortestpaths/FloydWarshall.java indy256/codelibrary/java/graphs/shortestpaths/FloydWarshall.java]&lt;br /&gt;
* [https://github.com/ADJA/algos/blob/master/Graphs/FloydWarshall.cpp ADJA/algos/Graphs/FloydWarshall.cpp]&lt;br /&gt;
* [https://github.com/kevin-wayne/algs4/blob/master/src/main/java/edu/princeton/cs/algs4/FloydWarshall.java kevin-wayne/algs4/FloydWarshall.java]&lt;br /&gt;
Задачи:&lt;br /&gt;
* [http://informatics.mccme.ru/course/view.php?id=6 informatics.mccme.ru &amp;amp;mdash; Курс &amp;amp;laquo;Алгоритмы на графах&amp;amp;raquo; — часть 5]&lt;br /&gt;
* [http://acmp.ru/?main=task&amp;amp;id_task=135 ACMP #135 &amp;amp;mdash; Алгоритм Флойда]&lt;br /&gt;
* [http://acmp.ru/?main=task&amp;amp;id_task=136 ACMP #136 &amp;amp;mdash; Алгоритм Флойда - 2]&lt;br /&gt;
* [http://acmp.ru/?main=task&amp;amp;id_task=137 ACMP #137 &amp;amp;mdash; Существование пути]&lt;br /&gt;
* [http://acmp.ru/?main=task&amp;amp;id_task=562 ACMP #562 &amp;amp;mdash; Слабая K-связность]&lt;br /&gt;
&lt;br /&gt;
[[Category:Кратчайшие пути между всеми парами вершин]]&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A4%D0%BB%D0%BE%D0%B9%D0%B4%D0%B0&amp;diff=2913</id>
		<title>Алгоритм Флойда</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A4%D0%BB%D0%BE%D0%B9%D0%B4%D0%B0&amp;diff=2913"/>
		<updated>2024-04-21T12:52:41Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: /* TLDR */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== TLDR ==&lt;br /&gt;
&amp;lt;youtube width=&amp;quot;300&amp;quot; height=&amp;quot;180&amp;quot;&amp;gt;c7ahlIwBL7o&amp;lt;/youtube&amp;gt;&lt;br /&gt;
&amp;lt;youtube width=&amp;quot;300&amp;quot; height=&amp;quot;180&amp;quot;&amp;gt;8JQ565Rz7d8&amp;lt;/youtube&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;youtube width=&amp;quot;300&amp;quot; height=&amp;quot;180&amp;quot;&amp;gt;LVnPNWwd-yo&amp;lt;/youtube&amp;gt;&lt;br /&gt;
&amp;lt;youtube width=&amp;quot;300&amp;quot; height=&amp;quot;180&amp;quot;&amp;gt;LnOOuNcRLIo&amp;lt;/youtube&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Код ==&lt;br /&gt;
&lt;br /&gt;
Ищем расстояния во взвешенном графе от каждой вершины до всех остальных. Веса могут быть любыми.&lt;br /&gt;
&lt;br /&gt;
Идея алгоритма:&lt;br /&gt;
* Инициализация. Пусть dist[a][b] — кратчайшее расстояние от a до b, но длиной не более чем одно ребро. Нетрудно понять, что dist — это просто матрица смежности графа (возможно, с небольшими модификациями):&lt;br /&gt;
:* Случай простого графа: dist[a][a] = 0, dist[a][b] = весу ребра (a, b); если ребра (a, b) нет, то dist[a][b] = INF;&lt;br /&gt;
:* Если есть кратные рёбра, то dist[a][b] = весу минимального ребра между a и b;&lt;br /&gt;
:* Если есть петли, то dist[a][a] = min(0, вес минимальной петли из a);&lt;br /&gt;
* Шаг 0. Теперь разрешаем путям проходить через вершину 0. Обновляем dist: dist[a][b] = min(dist[a][b], dist[a][0] + dist[0][b]);&lt;br /&gt;
* Шаг 1. Теперь разрешаем путям проходить через вершину 1. Обновляем dist: dist[a][b] = min(dist[a][b], dist[a][1] + dist[1][b]);&lt;br /&gt;
* Повторяем до шага vertexCount - 1.&lt;br /&gt;
&lt;br /&gt;
 const int INF = 1e9;&lt;br /&gt;
 vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; dist(vertexCount, vector&amp;lt;int&amp;gt;(vertexCount, INF));&lt;br /&gt;
 &lt;br /&gt;
 for (int v = 0; v &amp;lt; vertexCount; v++)&lt;br /&gt;
     dist[v][v] = 0;&lt;br /&gt;
 &lt;br /&gt;
 for (int i = 0; i &amp;lt; edgeCount; i++) {&lt;br /&gt;
     int a, b, w;&lt;br /&gt;
     cin &amp;gt;&amp;gt; a &amp;gt;&amp;gt; b &amp;gt;&amp;gt; w;&lt;br /&gt;
     dist[a][b] = min(dist[a][b], w);&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 for (int v = 0; v &amp;lt; vertexCount; v++) {&lt;br /&gt;
     for (int a = 0; a &amp;lt; vertexCount; a++) {&lt;br /&gt;
         for (int b = 0; b &amp;lt; vertexCount; b++) {&lt;br /&gt;
             if (dist[a][v] == INF || dist[v][b] == INF)&lt;br /&gt;
                 continue;&lt;br /&gt;
             dist[a][b] = min(dist[a][b], dist[a][v] + dist[v][b]);&lt;br /&gt;
             dist[a][b] = max(dist[a][b], -INF);&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
На что нужно обратить внимание:&lt;br /&gt;
* При инициализации матрицы на главной диагонали должны быть нули (если нет петель отрицательного веса), а там, где рёбер нет — бесконечности;&lt;br /&gt;
* При наличии отрицательных рёбер следует избегать присваиваний вида (INF - 1). Для этого, если хотя бы один из фрагментов пути равен INF, нужно делать continue;&lt;br /&gt;
* При наличии отрицательных циклов расстояния могут очень быстро уменьшаться и приводить к отрицательным переполнениям. Поэтому нужно ограничивать отрицательные числа снизу.&lt;br /&gt;
&lt;br /&gt;
== Отрицательные циклы ==&lt;br /&gt;
&lt;br /&gt;
Вершина v лежит на отрицательном цикле, если после запуска алгоритма Флойда dist[v][v] &amp;lt; 0. Наличие такой вершины — критерий отрицательного цикла в алгоритме Флойда.&lt;br /&gt;
&lt;br /&gt;
При наличии отрицательных циклов между некоторыми парами вершин может не существовать кратчайшего пути. Между вершинами a и b нет кратчайшего пути, если существует вершина v, лежащая на отрицательном цикле, которая достижима из a и из которой достижима b.&lt;br /&gt;
&lt;br /&gt;
 const int NO_SHORTEST_PATH = -2e9;&lt;br /&gt;
 &lt;br /&gt;
 for (int v = 0; v &amp;lt; vertexCount; v++)&lt;br /&gt;
     for (int a = 0; a &amp;lt; vertexCount; a++)&lt;br /&gt;
         for (int b = 0; b &amp;lt; vertexCount; b++)&lt;br /&gt;
             if (dist[a][v] != INF &amp;amp;&amp;amp; dist[v][v] &amp;lt; 0 &amp;amp;&amp;amp; dist[v][b] != INF)&lt;br /&gt;
                 dist[a][b] = NO_SHORTEST_PATH;&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
Теория:&lt;br /&gt;
* [http://e-maxx.ru/algo/floyd_warshall_algorithm e-maxx.ru — Алгоритм Флойда-Уоршелла]&lt;br /&gt;
* [https://cp-algorithms.com/graph/all-pair-shortest-path-floyd-warshall.html cp-algorithms.com — Floyd-Warshall Algorithm]&lt;br /&gt;
* [http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A4%D0%BB%D0%BE%D0%B9%D0%B4%D0%B0 neerc.ifmo.ru/wiki — Алгоритм Флойда]&lt;br /&gt;
* [http://brestprog.by/topics/floyd/ brestprog.by — Алгоритм Флойда-Уоршелла]&lt;br /&gt;
* [http://algorithmica.org/tg/shortest-paths algorithmica.org — Кратчайшие пути в графе]&lt;br /&gt;
* [http://brilliant.org/wiki/floyd-warshall-algorithm brilliant.org — Floyd-Warshall Algorithm]&lt;br /&gt;
* usaco.guide — [https://usaco.guide/gold/shortest-paths?lang=cpp#floyd-warshall Shortest Paths with Non-Negative Edge Weights: Floyd-Warshall], [https://usaco.guide/adv/sp-neg?lang=cpp#floyd-warshall Shortest Paths with Negative Edge Weights: Floyd-Warshall]&lt;br /&gt;
Демонстрация:&lt;br /&gt;
* [https://www.cs.usfca.edu/~galles/visualization/Floyd.html www.cs.usfca.edu — Floyd-Warshall All-Pairs Shortest Path]&lt;br /&gt;
Код:&lt;br /&gt;
* [https://github.com/indy256/codelibrary/blob/master/java/graphs/shortestpaths/FloydWarshall.java indy256/codelibrary/java/graphs/shortestpaths/FloydWarshall.java]&lt;br /&gt;
* [https://github.com/ADJA/algos/blob/master/Graphs/FloydWarshall.cpp ADJA/algos/Graphs/FloydWarshall.cpp]&lt;br /&gt;
* [https://github.com/kevin-wayne/algs4/blob/master/src/main/java/edu/princeton/cs/algs4/FloydWarshall.java kevin-wayne/algs4/FloydWarshall.java]&lt;br /&gt;
Задачи:&lt;br /&gt;
* [http://informatics.mccme.ru/course/view.php?id=6 informatics.mccme.ru &amp;amp;mdash; Курс &amp;amp;laquo;Алгоритмы на графах&amp;amp;raquo; — часть 5]&lt;br /&gt;
* [http://acmp.ru/?main=task&amp;amp;id_task=135 ACMP #135 &amp;amp;mdash; Алгоритм Флойда]&lt;br /&gt;
* [http://acmp.ru/?main=task&amp;amp;id_task=136 ACMP #136 &amp;amp;mdash; Алгоритм Флойда - 2]&lt;br /&gt;
* [http://acmp.ru/?main=task&amp;amp;id_task=137 ACMP #137 &amp;amp;mdash; Существование пути]&lt;br /&gt;
* [http://acmp.ru/?main=task&amp;amp;id_task=562 ACMP #562 &amp;amp;mdash; Слабая K-связность]&lt;br /&gt;
&lt;br /&gt;
[[Category:Кратчайшие пути между всеми парами вершин]]&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=%D0%A5%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D1%81%D1%82%D1%80%D0%BE%D0%BA&amp;diff=2912</id>
		<title>Хеширование строк</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=%D0%A5%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D1%81%D1%82%D1%80%D0%BE%D0%BA&amp;diff=2912"/>
		<updated>2024-03-05T00:32:11Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;h&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt; = s&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt;x&amp;lt;sup&amp;gt;n - 1&amp;lt;/sup&amp;gt; + s&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt;x&amp;lt;sup&amp;gt;n - 2&amp;lt;/sup&amp;gt; + ... + s&amp;lt;sub&amp;gt;n - 2&amp;lt;/sub&amp;gt;x + s&amp;lt;sub&amp;gt;n - 1&amp;lt;/sub&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 struct Hasher {&lt;br /&gt;
     long long x, mod;&lt;br /&gt;
     vector&amp;lt;long long&amp;gt; p, h;&lt;br /&gt;
 &lt;br /&gt;
     Hasher(const string &amp;amp;s, long long x = 31, long long mod = 1e9 + 7) : x(x), mod(mod) {&lt;br /&gt;
         p.resize(s.size());&lt;br /&gt;
         h.resize(s.size());&lt;br /&gt;
 &lt;br /&gt;
         p[0] = 1;&lt;br /&gt;
         h[0] = s[0] - &#039;a&#039; + 1;&lt;br /&gt;
 &lt;br /&gt;
         for (int i = 1; i &amp;lt; s.size(); i++) {&lt;br /&gt;
             p[i] = p[i - 1] * x % mod;&lt;br /&gt;
             h[i] = (h[i - 1] * x % mod + s[i] - &#039;a&#039; + 1) % mod;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     long long getHash(int l, int r) {&lt;br /&gt;
         long long res = h[r];&lt;br /&gt;
         if (l)&lt;br /&gt;
             res = (res - p[r - l + 1] * h[l - 1] % mod + mod) % mod;&lt;br /&gt;
         return res;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
{| width=&amp;quot;100%&amp;quot;&lt;br /&gt;
| width=50% |&lt;br /&gt;
h&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt; = s&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; + s&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt;x + s&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt;x&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt; + ... + s&amp;lt;sub&amp;gt;n - 1&amp;lt;/sub&amp;gt;x&amp;lt;sup&amp;gt;n - 1&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для выравнивания хеши различных подстрок домножаются на x&amp;lt;sup&amp;gt;n - 1 - L&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 struct Hasher {&lt;br /&gt;
     long long x = 31, mod = 1e9 + 7;&lt;br /&gt;
     vector&amp;lt;long long&amp;gt; p, h;&lt;br /&gt;
 &lt;br /&gt;
     Hasher(const string &amp;amp;s) {&lt;br /&gt;
         p.resize(s.size());&lt;br /&gt;
         h.resize(s.size());&lt;br /&gt;
 &lt;br /&gt;
         p[0] = 1;&lt;br /&gt;
         h[0] = s[0] - &#039;a&#039; + 1;&lt;br /&gt;
 &lt;br /&gt;
         for (int i = 1; i &amp;lt; s.size(); i++) {&lt;br /&gt;
             p[i] = p[i - 1] * x % mod;&lt;br /&gt;
             h[i] = (h[i - 1] + p[i] * (s[i] - &#039;a&#039; + 1) % mod) % mod;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     long long getHash(int l, int r) {&lt;br /&gt;
         long long res = h[r];&lt;br /&gt;
         if (l)&lt;br /&gt;
             res = (res - h[l - 1] + mod) % mod;&lt;br /&gt;
         res = res * p[p.size() - 1 - l] % mod;&lt;br /&gt;
         return res;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
Этот способ непригоден для сравнения участков двух строк разной длины. В данном случае хеш более левого участка следует домножать на x&amp;lt;sup&amp;gt;|L1 - L2|&amp;lt;/sup&amp;gt;.&lt;br /&gt;
 &lt;br /&gt;
| width=&amp;quot;10px&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| width=50% |&lt;br /&gt;
h&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt; = s&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; + s&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt;x + s&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt;x&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt; + ... + s&amp;lt;sub&amp;gt;n - 1&amp;lt;/sub&amp;gt;x&amp;lt;sup&amp;gt;n - 1&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для выравнивания хеши различных подстрок домножаются на (x&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;)&amp;lt;sup&amp;gt;L&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 struct Hasher {&lt;br /&gt;
     long long x = 31, {{Changed|1=xi = 129032259,}} mod = 1e9 + 7;&lt;br /&gt;
     vector&amp;lt;long long&amp;gt; p, {{Changed|pi,}} h;&lt;br /&gt;
 &lt;br /&gt;
     Hasher(const string &amp;amp;s) {&lt;br /&gt;
         p.resize(s.size());&lt;br /&gt;
         {{Changed|pi.resize(s.size());}}&lt;br /&gt;
         h.resize(s.size());&lt;br /&gt;
 &lt;br /&gt;
         p[0] = {{Changed|1=pi[0] = }} 1;&lt;br /&gt;
         h[0] = s[0] - &#039;a&#039; + 1;&lt;br /&gt;
 &lt;br /&gt;
         for (int i = 1; i &amp;lt; s.size(); i++) {&lt;br /&gt;
             p[i] = p[i - 1] * x % mod;&lt;br /&gt;
             {{Changed|1=pi[i] = pi[i - 1] * xi % mod;}}&lt;br /&gt;
             h[i] = (h[i - 1] + p[i] * (s[i] - &#039;a&#039; + 1) % mod) % mod;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     long long getHash(int l, int r) {&lt;br /&gt;
         long long res = h[r];&lt;br /&gt;
         if (l) {&lt;br /&gt;
             res = (res - h[l - 1] + mod) % mod;&lt;br /&gt;
             {{Changed|1=res = (res * pi[l]) % mod}};&lt;br /&gt;
         }&lt;br /&gt;
         return res;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
Теория:&lt;br /&gt;
* [http://e-maxx.ru/algo/string_hashes e-maxx.ru &amp;amp;mdash; Алгоритмы хэширования в задачах на строки]&lt;br /&gt;
* [http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8_%D0%B2_%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B5_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%85%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F._%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A0%D0%B0%D0%B1%D0%B8%D0%BD%D0%B0-%D0%9A%D0%B0%D1%80%D0%BF%D0%B0 neerc.ifmo.ru &amp;amp;mdash; Поиск подстроки в строке с использованием хеширования. Алгоритм Рабина-Карпа]&lt;br /&gt;
* [http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BD%D0%B0%D0%B8%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B5%D0%B9_%D0%BE%D0%B1%D1%89%D0%B5%D0%B9_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8_%D0%B4%D0%B2%D1%83%D1%85_%D1%81%D1%82%D1%80%D0%BE%D0%BA_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%85%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F neerc.ifmo.ru &amp;amp;mdash; Поиск наибольшей общей подстроки двух строк с использованием хеширования]&lt;br /&gt;
* [https://ru.algorithmica.org/cs/hashing/polynomial/ ru.algorithmica.org — Полиномиальное хеширование]&lt;br /&gt;
* [http://habrahabr.ru/post/142589/ habrahabr.ru &amp;amp;mdash; Полиномиальные хеши и их применение]&lt;br /&gt;
* [http://codeforces.com/blog/entry/4898 codeforces.com &amp;amp;mdash; Anti-hash test]&lt;br /&gt;
* [http://codeforces.com/blog/entry/60445 codeforces.com — Полиномиальное хеширование + разбор интересных задач]&lt;br /&gt;
* [https://blog.algoprog.ru/hash-no-multiply/ Калинин П. — Про хеширование без домножения]&lt;br /&gt;
* [https://usaco.guide/gold/string-hashing?lang=cpp usaco.guide — String Hashing]&lt;br /&gt;
Код:&lt;br /&gt;
* [https://github.com/indy256/codelibrary/blob/master/cpp/strings/hashing.cpp codelibrary/cpp/strings/hashing.cpp]&lt;br /&gt;
* [https://github.com/ADJA/algos/blob/master/Strings/Hashing.cpp algos/Strings/Hashing.cpp]&lt;br /&gt;
Задачи:&lt;br /&gt;
* [[:Категория: Задачи: Хеширование строк|Задачи: Хеширование строк]]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Строки]]&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=%D0%9A%D0%B0%D1%82%D0%B5%D0%B3%D0%BE%D1%80%D0%B8%D1%8F:%D0%A3%D1%87%D0%B5%D0%B1%D0%BD%D1%8B%D0%B9_%D0%BA%D1%83%D1%80%D1%81_%C2%AB%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D1%8B_%D0%B8_%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D1%8B_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85%C2%BB&amp;diff=2911</id>
		<title>Категория:Учебный курс «Алгоритмы и структуры данных»</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=%D0%9A%D0%B0%D1%82%D0%B5%D0%B3%D0%BE%D1%80%D0%B8%D1%8F:%D0%A3%D1%87%D0%B5%D0%B1%D0%BD%D1%8B%D0%B9_%D0%BA%D1%83%D1%80%D1%81_%C2%AB%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D1%8B_%D0%B8_%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D1%8B_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85%C2%BB&amp;diff=2911"/>
		<updated>2024-02-16T22:21:09Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&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;
:* [[Поразрядная сортировка]]&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;
:* [[Множество. Реализация на битовых векторах]]&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;
:* 📄 [[Sparse table]]&lt;br /&gt;
:* Sqrt-декомпозиция&lt;br /&gt;
:* [[Алгоритм Мо]]&lt;br /&gt;
|width=450px|&lt;br /&gt;
;Алгоритмы для работы с графами&lt;br /&gt;
:&lt;br /&gt;
:* [[Основные определения. Представление графов]]&lt;br /&gt;
: Поиск в глубину и его приложения&lt;br /&gt;
:* 📄 [[Поиск в глубину]]&amp;lt;sup&amp;gt;&#039;&#039;O(V+E)&#039;&#039;&amp;lt;/sup&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;
:* 📄 [[Поиск в ширину]]&amp;lt;sup&amp;gt;&#039;&#039;O(V+E)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* 📝 [[Алгоритм Дейкстры]]&amp;lt;sup&amp;gt;&#039;&#039;O(V&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;+E) или O(ElogV)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* 📄 [[Алгоритм A*]]&lt;br /&gt;
:* [[Алгоритм Форда-Беллмана]]&amp;lt;sup&amp;gt;&#039;&#039;O(VE)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* [[Кратчайшие пути в ациклических орграфах]]&amp;lt;sup&amp;gt;&#039;&#039;O(V+E)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
: Кратчайшие пути между всеми парами вершин&lt;br /&gt;
:* 📄 [[Алгоритм Флойда]]&amp;lt;sup&amp;gt;&#039;&#039;O(V&amp;lt;sup&amp;gt;3&amp;lt;/sup&amp;gt;)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* [[Алгоритм Джонсона]]&amp;lt;sup&amp;gt;&#039;&#039;O(VElogV)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
: Минимальное остовное дерево&lt;br /&gt;
:* 📝 [[Алгоритм Краскала]]&amp;lt;sup&amp;gt;&#039;&#039;O(ElogV)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* 📄 [[Алгоритм Прима]]&amp;lt;sup&amp;gt;&#039;&#039;O(V&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;+E) или O(ElogV)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
: Максимальный поток&lt;br /&gt;
:* 📄 [[Алгоритм Форда-Фалкерсона]]&amp;lt;sup&amp;gt;&#039;&#039;O(Flow&amp;amp;times;E)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* 📄 [[Алгоритм Эдмондса-Карпа]]&amp;lt;sup&amp;gt;&#039;&#039;O(VE&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* 📄 [[Алгоритм Диница]]&amp;lt;sup&amp;gt;&#039;&#039;O(V&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;E)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* 📝 [[Максимальный поток минимальной стоимости]]&lt;br /&gt;
:* [[Применения максимального потока]]&lt;br /&gt;
: Максимальное паросочетание&lt;br /&gt;
:* 📄 [[Алгоритм Куна]]&amp;lt;sup&amp;gt;&#039;&#039;O(VE)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* 📄 [[Минимальное вершинное покрытие, максимальное независимое множество]]&lt;br /&gt;
: Наименьший общий предок&lt;br /&gt;
:* 📄 [[Метод двоичного подъёма]]&amp;lt;sup&amp;gt;&#039;&#039;O(NlogN), O(logN)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
:* [[Сведение LCA к RMQ и RMQ к LCA]]&lt;br /&gt;
:* [[Алгоритм Тарьяна (offline)]]&amp;lt;sup&amp;gt;&#039;&#039;O(N), O(1)&#039;&#039;&amp;lt;/sup&amp;gt;&lt;br /&gt;
: Декомпозиции деревьев&lt;br /&gt;
:* 📝 [[Heavy-light-декомпозиция]]&lt;br /&gt;
|&lt;br /&gt;
;Полный перебор и методы его оптимизации&lt;br /&gt;
:&lt;br /&gt;
:* [[Полный перебор]]&lt;br /&gt;
:* [[Два указателя]]&lt;br /&gt;
:* Meet in the middle&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;
:* 📄 [[Метод Гаусса]]&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;
:* 📄 [[Хеширование строк]]&lt;br /&gt;
:* 📄 [[Префикс-функция]]&lt;br /&gt;
:* 📄 [[Алгоритм Ахо-Корасик]]&lt;br /&gt;
:* 📄 [[Z-функция]]&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;
&#039;&#039;&amp;amp;copy; В. А. Фолунин, 2012–2024&#039;&#039;&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B2%D0%BE%D0%B7%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B2_%D1%81%D1%82%D0%B5%D0%BF%D0%B5%D0%BD%D1%8C&amp;diff=2910</id>
		<title>Быстрое возведение в степень</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B2%D0%BE%D0%B7%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B2_%D1%81%D1%82%D0%B5%D0%BF%D0%B5%D0%BD%D1%8C&amp;diff=2910"/>
		<updated>2023-09-17T16:09:56Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Базовая реализация ==&lt;br /&gt;
&lt;br /&gt;
 long long binPow(long long x, long long p, long long mod) {&lt;br /&gt;
     if (!p)&lt;br /&gt;
         return 1;&lt;br /&gt;
     if (p % 2)&lt;br /&gt;
         return binPow(x, p - 1, mod) * x % mod;&lt;br /&gt;
     long long r = binPow(x, p / 2, mod);&lt;br /&gt;
     return r * r % mod;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Вычисление степеней, превышающих размер long long ==&lt;br /&gt;
&lt;br /&gt;
Если x и mod взаимно просты, то binPow(x, p, mod) == binPow(x, p % phi(mod), mod), где phi — функция Эйлера.&lt;br /&gt;
&lt;br /&gt;
Если x и mod не взаимно просты, но p &amp;amp;ge; log&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt;(mod), то binPow(x, p, mod) == binPow(x, p % phi(mod) + phi(mod), mod).&lt;br /&gt;
&lt;br /&gt;
 long long phi(long long n) {&lt;br /&gt;
     long long res = n;&lt;br /&gt;
     for (long long d = 2; d * d &amp;lt;= n; d++) {&lt;br /&gt;
         if (n % d == 0) {&lt;br /&gt;
             while (n % d == 0)&lt;br /&gt;
                 n /= d;&lt;br /&gt;
             res -= res / d;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
     if (n &amp;gt; 1)&lt;br /&gt;
         res -= res / n;&lt;br /&gt;
     return res;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Возведение в степень матриц ==&lt;br /&gt;
&lt;br /&gt;
 const long long MOD = 1e9 + 7;&lt;br /&gt;
 &lt;br /&gt;
 using Matrix = vector&amp;lt;vector&amp;lt;long long&amp;gt;&amp;gt;;&lt;br /&gt;
 &lt;br /&gt;
 Matrix identityMatrix(int size) {&lt;br /&gt;
     Matrix res(size, vector&amp;lt;long long&amp;gt;(size));&lt;br /&gt;
     for (int i = 0; i &amp;lt; res.size(); i++)&lt;br /&gt;
         res[i][i] = 1;&lt;br /&gt;
     return res;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 Matrix operator * (const Matrix &amp;amp;a, const Matrix &amp;amp;b) {&lt;br /&gt;
     Matrix res(a.size(), vector&amp;lt;long long&amp;gt;(b[0].size()));&lt;br /&gt;
     for (int y = 0; y &amp;lt; res.size(); y++)&lt;br /&gt;
         for (int x = 0; x &amp;lt; res[0].size(); x++)&lt;br /&gt;
             for (int i = 0; i &amp;lt; a[0].size(); i++)&lt;br /&gt;
                 res[y][x] = (res[y][x] + a[y][i] * b[i][x] % MOD) % MOD;&lt;br /&gt;
     return res;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 Matrix pow(const Matrix &amp;amp;m, long long p) {&lt;br /&gt;
     if (!p)&lt;br /&gt;
         return identityMatrix(m.size());&lt;br /&gt;
     if (p % 2)&lt;br /&gt;
         return pow(m, p - 1) * m;&lt;br /&gt;
     Matrix r = pow(m, p / 2);&lt;br /&gt;
     return r * r;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
* [http://e-maxx.ru/algo/binary_pow E-maxx — Бинарное возведение в степень]&lt;br /&gt;
* [https://cp-algorithms.com/algebra/binary-exp.html cp-algorithms.com — Binary Exponentiation]&lt;br /&gt;
* [https://cp-algorithms.com/algebra/phi-function.html#application cp-algorithms.com — Euler&#039;s totient function application in Euler&#039;s theorem]&lt;br /&gt;
* [http://algorithmica.org/tg/number-theory algorithmica.org — Теория чисел]&lt;br /&gt;
* [http://codeforces.com/blog/entry/43225 Codeforces — Cool tricks using Matrix Exponential]&lt;br /&gt;
* [http://habr.com/ru/post/148901 Habr — Используем быстрое возведение матриц в степень для написания очень быстрого интерпретатора простого языка программирования]&lt;br /&gt;
* [http://habr.com/ru/post/236689 Habr — Автоматическая оптимизация алгоритмов с помощью быстрого возведения матриц в степень]&lt;br /&gt;
&lt;br /&gt;
[[Category:Арифметические алгоритмы]]&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=%D0%A7%D0%B0%D1%81%D1%82%D0%BE_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D1%83%D0%B5%D0%BC%D1%8B%D0%B5_%D1%84%D1%80%D0%B0%D0%B3%D0%BC%D0%B5%D0%BD%D1%82%D1%8B&amp;diff=2909</id>
		<title>Часто используемые фрагменты</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=%D0%A7%D0%B0%D1%81%D1%82%D0%BE_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D1%83%D0%B5%D0%BC%D1%8B%D0%B5_%D1%84%D1%80%D0%B0%D0%B3%D0%BC%D0%B5%D0%BD%D1%82%D1%8B&amp;diff=2909"/>
		<updated>2023-09-13T17:25:20Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Split и join ==&lt;br /&gt;
&lt;br /&gt;
{| width=&amp;quot;100%&amp;quot;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 vector&amp;lt;string&amp;gt; split(string &amp;amp;line) {&lt;br /&gt;
     vector&amp;lt;string&amp;gt; words;&lt;br /&gt;
     string word;&lt;br /&gt;
     for (char c : line) {&lt;br /&gt;
         if (c != &#039; &#039;) {&lt;br /&gt;
             word += c;&lt;br /&gt;
         } else if (!word.empty()) {&lt;br /&gt;
             words.push_back(word);&lt;br /&gt;
             word.clear();&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
     if (!word.empty())&lt;br /&gt;
         words.push_back(word);&lt;br /&gt;
     return words;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
| width=&amp;quot;10px&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 string join(vector&amp;lt;string&amp;gt; &amp;amp;words) {&lt;br /&gt;
     string line;&lt;br /&gt;
     for (int i = 0; i &amp;lt; words.size(); i++)&lt;br /&gt;
         line += words[i] + (i + 1 &amp;lt; words.size() ? &amp;quot; &amp;quot; : &amp;quot;&amp;quot;);&lt;br /&gt;
     return line;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| width=&amp;quot;100%&amp;quot;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 vector&amp;lt;string&amp;gt; split(string &amp;amp;line, const string &amp;amp;separators) {&lt;br /&gt;
     vector&amp;lt;string&amp;gt; words;&lt;br /&gt;
     string word;&lt;br /&gt;
     for (char c : line) {&lt;br /&gt;
         if (separators.find(c) == -1) {&lt;br /&gt;
             word += c;&lt;br /&gt;
         } else if (!word.empty()) {&lt;br /&gt;
             words.push_back(word);&lt;br /&gt;
             word.clear();&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
     if (!word.empty())&lt;br /&gt;
         words.push_back(word);&lt;br /&gt;
     return words;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
| width=&amp;quot;10px&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 string join(vector&amp;lt;string&amp;gt; &amp;amp;words, const string &amp;amp;separator) {&lt;br /&gt;
     string line;&lt;br /&gt;
     for (int i = 0; i &amp;lt; words.size(); i++)&lt;br /&gt;
         line += words[i] + (i + 1 &amp;lt; words.size() ? separator : &amp;quot;&amp;quot;);&lt;br /&gt;
     return line;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Сумма цифр ==&lt;br /&gt;
{| width=&amp;quot;100%&amp;quot;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 int digitSum(int n) {&lt;br /&gt;
     int sum = 0;&lt;br /&gt;
     while (n) {&lt;br /&gt;
         sum += n % 10;&lt;br /&gt;
         n /= 10;&lt;br /&gt;
     }&lt;br /&gt;
     return sum;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
| width=&amp;quot;10px&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 int ones(int n) {&lt;br /&gt;
     int res = 0;&lt;br /&gt;
     while (n) {&lt;br /&gt;
         res++;&lt;br /&gt;
         n &amp;amp;= n - 1;&lt;br /&gt;
     }&lt;br /&gt;
     return res;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Перевод систем счисления ==&lt;br /&gt;
&lt;br /&gt;
{| width=&amp;quot;100%&amp;quot;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 long long toDec(string s, int base) {&lt;br /&gt;
     static const string DIGITS = &amp;quot;0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ&amp;quot;;&lt;br /&gt;
     long long res = 0;&lt;br /&gt;
     for (char c : s)&lt;br /&gt;
         res = res * base + DIGITS.find(c);&lt;br /&gt;
     return res;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
| width=&amp;quot;10px&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 string fromDec(long long n, int base) {&lt;br /&gt;
     static const string DIGITS = &amp;quot;0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ&amp;quot;;&lt;br /&gt;
     string res;&lt;br /&gt;
     while (n &amp;gt;= base) {&lt;br /&gt;
         res += DIGITS[n % base];&lt;br /&gt;
         n /= base;&lt;br /&gt;
     }&lt;br /&gt;
     res += DIGITS[n];&lt;br /&gt;
     reverse(res.begin(), res.end());&lt;br /&gt;
     return res;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Римская система счисления ==&lt;br /&gt;
&lt;br /&gt;
{| width=&amp;quot;100%&amp;quot;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 string toRoman(int n) {&lt;br /&gt;
     static const vector&amp;lt;pair&amp;lt;string, int&amp;gt;&amp;gt; CODES = {&lt;br /&gt;
         {&amp;quot;M&amp;quot;, 1000}, {&amp;quot;CM&amp;quot;, 900}, {&amp;quot;D&amp;quot;, 500}, {&amp;quot;CD&amp;quot;, 400},&lt;br /&gt;
         {&amp;quot;C&amp;quot;, 100}, {&amp;quot;XC&amp;quot;, 90}, {&amp;quot;L&amp;quot;, 50}, {&amp;quot;XL&amp;quot;, 40},&lt;br /&gt;
         {&amp;quot;X&amp;quot;, 10}, {&amp;quot;IX&amp;quot;, 9}, {&amp;quot;V&amp;quot;, 5}, {&amp;quot;IV&amp;quot;, 4}, {&amp;quot;I&amp;quot;, 1}&lt;br /&gt;
     };&lt;br /&gt;
     &lt;br /&gt;
     string res;&lt;br /&gt;
     for (auto &amp;amp;[code, value] : CODES) {&lt;br /&gt;
         while (n &amp;gt;= value) {&lt;br /&gt;
             n -= value;&lt;br /&gt;
             res += code;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
     return res;        &lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
| width=&amp;quot;10px&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 int fromRoman(string &amp;amp;s) {&lt;br /&gt;
     static const vector&amp;lt;pair&amp;lt;string, int&amp;gt;&amp;gt; CODES = {&lt;br /&gt;
         {&amp;quot;M&amp;quot;, 1000}, {&amp;quot;CM&amp;quot;, 900}, {&amp;quot;D&amp;quot;, 500}, {&amp;quot;CD&amp;quot;, 400},&lt;br /&gt;
         {&amp;quot;C&amp;quot;, 100}, {&amp;quot;XC&amp;quot;, 90}, {&amp;quot;L&amp;quot;, 50}, {&amp;quot;XL&amp;quot;, 40},&lt;br /&gt;
         {&amp;quot;X&amp;quot;, 10}, {&amp;quot;IX&amp;quot;, 9}, {&amp;quot;V&amp;quot;, 5}, {&amp;quot;IV&amp;quot;, 4}, {&amp;quot;I&amp;quot;, 1}&lt;br /&gt;
     };&lt;br /&gt;
     &lt;br /&gt;
     int pos = 0, res = 0;&lt;br /&gt;
     for (auto &amp;amp;[code, value] : CODES) {&lt;br /&gt;
         while (s.substr(pos, code.size()) == code) {&lt;br /&gt;
             pos += code.size();&lt;br /&gt;
             res += value;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
     return res;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Задача Иосифа ==&lt;br /&gt;
&lt;br /&gt;
 int size, step;&lt;br /&gt;
 cin &amp;gt;&amp;gt; size &amp;gt;&amp;gt; step;&lt;br /&gt;
 &lt;br /&gt;
 vector&amp;lt;int&amp;gt; a(size);&lt;br /&gt;
 iota(a.begin(), a.end(), 1);&lt;br /&gt;
 &lt;br /&gt;
 int pos = 0;&lt;br /&gt;
 while (!a.empty()) {&lt;br /&gt;
     pos = (pos + step - 1) % a.size();&lt;br /&gt;
     cout &amp;lt;&amp;lt; a[pos] &amp;lt;&amp;lt; &amp;quot; &amp;quot;;&lt;br /&gt;
     a.erase(a.begin() + pos);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Даты ==&lt;br /&gt;
&lt;br /&gt;
 struct Date {&lt;br /&gt;
     int day, month, year, dayOfWeek;&lt;br /&gt;
 &lt;br /&gt;
     bool isLeap(int year) {&lt;br /&gt;
         return year % 400 == 0 || year % 100 &amp;amp;&amp;amp; year % 4 == 0;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     int daysInMonth(int month, int year) {&lt;br /&gt;
         vector&amp;lt;int&amp;gt; days = { 0, 31, 28 + isLeap(year), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };&lt;br /&gt;
         return days[month];&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     Date &amp;amp;operator++(int) {&lt;br /&gt;
         day++;&lt;br /&gt;
         if (day &amp;gt; daysInMonth(month, year)) {&lt;br /&gt;
             day = 1;&lt;br /&gt;
             month++;&lt;br /&gt;
             if (month &amp;gt; 12) {&lt;br /&gt;
                 month = 1;&lt;br /&gt;
                 year++;&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         dayOfWeek = (dayOfWeek + 1) % 7;&lt;br /&gt;
         return *this;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     friend ostream &amp;amp;operator &amp;lt;&amp;lt; (ostream &amp;amp;out, Date &amp;amp;date) {&lt;br /&gt;
         static vector&amp;lt;string&amp;gt; names = {&lt;br /&gt;
             &amp;quot;Monday&amp;quot;, &amp;quot;Tuesday&amp;quot;, &amp;quot;Wednesday&amp;quot;, &amp;quot;Thursday&amp;quot;, &amp;quot;Friday&amp;quot;, &amp;quot;Saturday&amp;quot;, &amp;quot;Sunday&amp;quot;&lt;br /&gt;
         };&lt;br /&gt;
         out &amp;lt;&amp;lt; names[date.dayOfWeek] &amp;lt;&amp;lt; &amp;quot;, &amp;quot;;&lt;br /&gt;
         out &amp;lt;&amp;lt; setw(2) &amp;lt;&amp;lt; setfill(&#039;0&#039;) &amp;lt;&amp;lt; date.day &amp;lt;&amp;lt; &amp;quot;.&amp;quot;;&lt;br /&gt;
         out &amp;lt;&amp;lt; setw(2) &amp;lt;&amp;lt; setfill(&#039;0&#039;) &amp;lt;&amp;lt; date.month;&lt;br /&gt;
         return out;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
== Время ==&lt;br /&gt;
&lt;br /&gt;
{| width=&amp;quot;100%&amp;quot;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 int readTime() {&lt;br /&gt;
     int hours, minutes;&lt;br /&gt;
     char colon;&lt;br /&gt;
     cin &amp;gt;&amp;gt; hours &amp;gt;&amp;gt; colon &amp;gt;&amp;gt; minutes;&lt;br /&gt;
 &lt;br /&gt;
     return hours * 60 + minutes;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
| width=&amp;quot;10px&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 void writeTime(int time) {&lt;br /&gt;
     cout &amp;lt;&amp;lt; setw(2) &amp;lt;&amp;lt; setfill(&#039;0&#039;) &amp;lt;&amp;lt; time / 60 &amp;lt;&amp;lt; &amp;quot;:&amp;quot;;&lt;br /&gt;
     cout &amp;lt;&amp;lt; setw(2) &amp;lt;&amp;lt; setfill(&#039;0&#039;) &amp;lt;&amp;lt; time % 60;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Дроби ==&lt;br /&gt;
&lt;br /&gt;
 struct Fraction {&lt;br /&gt;
     long long num, den;&lt;br /&gt;
 &lt;br /&gt;
     Fraction(long long n = 0, long long d = 1) : num(n), den(d) {&lt;br /&gt;
         long long g = gcd(num, den);&lt;br /&gt;
         num /= g;&lt;br /&gt;
         den /= g;&lt;br /&gt;
 &lt;br /&gt;
         if (den &amp;lt; 0) {&lt;br /&gt;
             num = -num;&lt;br /&gt;
             den = -den;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     Fraction operator + (const Fraction &amp;amp;that) const {&lt;br /&gt;
         return { num * that.den + that.num * den, den * that.den };&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     Fraction operator - (const Fraction &amp;amp;that) const {&lt;br /&gt;
         return { num * that.den - that.num * den, den * that.den };&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     Fraction operator * (const Fraction &amp;amp;that) const {&lt;br /&gt;
         return { num * that.num, den * that.den };&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     Fraction operator / (const Fraction &amp;amp;that) const {&lt;br /&gt;
         return { num * that.den, den * that.num };&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     friend istream &amp;amp;operator &amp;gt;&amp;gt; (istream &amp;amp;in, Fraction &amp;amp;f) {&lt;br /&gt;
         long long num, den = 1;&lt;br /&gt;
         in &amp;gt;&amp;gt; num;&lt;br /&gt;
         if (in.peek() == &#039;/&#039;) {&lt;br /&gt;
             in.ignore();&lt;br /&gt;
             in &amp;gt;&amp;gt; den;&lt;br /&gt;
         }&lt;br /&gt;
         f = { num, den };&lt;br /&gt;
         return in;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     friend ostream &amp;amp;operator &amp;lt;&amp;lt; (ostream &amp;amp;out, const Fraction &amp;amp;f) {&lt;br /&gt;
         out &amp;lt;&amp;lt; f.num;&lt;br /&gt;
         if (f.den != 1)&lt;br /&gt;
             out &amp;lt;&amp;lt; &amp;quot;/&amp;quot; &amp;lt;&amp;lt; f.den;&lt;br /&gt;
         return out;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=%D0%A7%D0%B0%D1%81%D1%82%D0%BE_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D1%83%D0%B5%D0%BC%D1%8B%D0%B5_%D1%84%D1%80%D0%B0%D0%B3%D0%BC%D0%B5%D0%BD%D1%82%D1%8B&amp;diff=2908</id>
		<title>Часто используемые фрагменты</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=%D0%A7%D0%B0%D1%81%D1%82%D0%BE_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D1%83%D0%B5%D0%BC%D1%8B%D0%B5_%D1%84%D1%80%D0%B0%D0%B3%D0%BC%D0%B5%D0%BD%D1%82%D1%8B&amp;diff=2908"/>
		<updated>2023-09-11T23:04:39Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: /* Время */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Split и join ==&lt;br /&gt;
&lt;br /&gt;
{| width=&amp;quot;100%&amp;quot;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 vector&amp;lt;string&amp;gt; split(string &amp;amp;line) {&lt;br /&gt;
     vector&amp;lt;string&amp;gt; words;&lt;br /&gt;
     string word;&lt;br /&gt;
     for (char c : line) {&lt;br /&gt;
         if (c != &#039; &#039;) {&lt;br /&gt;
             word += c;&lt;br /&gt;
         } else if (!word.empty()) {&lt;br /&gt;
             words.push_back(word);&lt;br /&gt;
             word.clear();&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
     if (!word.empty())&lt;br /&gt;
         words.push_back(word);&lt;br /&gt;
     return words;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
| width=&amp;quot;10px&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 string join(vector&amp;lt;string&amp;gt; &amp;amp;words) {&lt;br /&gt;
     string line;&lt;br /&gt;
     for (int i = 0; i &amp;lt; words.size(); i++)&lt;br /&gt;
         line += words[i] + (i + 1 &amp;lt; words.size() ? &amp;quot; &amp;quot; : &amp;quot;&amp;quot;);&lt;br /&gt;
     return line;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| width=&amp;quot;100%&amp;quot;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 vector&amp;lt;string&amp;gt; split(string &amp;amp;line, const string &amp;amp;separators) {&lt;br /&gt;
     vector&amp;lt;string&amp;gt; words;&lt;br /&gt;
     string word;&lt;br /&gt;
     for (char c : line) {&lt;br /&gt;
         if (separators.find(c) == -1) {&lt;br /&gt;
             word += c;&lt;br /&gt;
         } else if (!word.empty()) {&lt;br /&gt;
             words.push_back(word);&lt;br /&gt;
             word.clear();&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
     if (!word.empty())&lt;br /&gt;
         words.push_back(word);&lt;br /&gt;
     return words;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
| width=&amp;quot;10px&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 string join(vector&amp;lt;string&amp;gt; &amp;amp;words, const string &amp;amp;separator) {&lt;br /&gt;
     string line;&lt;br /&gt;
     for (int i = 0; i &amp;lt; words.size(); i++)&lt;br /&gt;
         line += words[i] + (i + 1 &amp;lt; words.size() ? separator : &amp;quot;&amp;quot;);&lt;br /&gt;
     return line;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Сумма цифр ==&lt;br /&gt;
{| width=&amp;quot;100%&amp;quot;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 int digitSum(int n) {&lt;br /&gt;
     int sum = 0;&lt;br /&gt;
     while (n) {&lt;br /&gt;
         sum += n % 10;&lt;br /&gt;
         n /= 10;&lt;br /&gt;
     }&lt;br /&gt;
     return sum;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
| width=&amp;quot;10px&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 int ones(int n) {&lt;br /&gt;
     int res = 0;&lt;br /&gt;
     while (n) {&lt;br /&gt;
         res++;&lt;br /&gt;
         n &amp;amp;= n - 1;&lt;br /&gt;
     }&lt;br /&gt;
     return res;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Перевод систем счисления ==&lt;br /&gt;
&lt;br /&gt;
{| width=&amp;quot;100%&amp;quot;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 long long toDec(string s, int base) {&lt;br /&gt;
     static const string DIGITS = &amp;quot;0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ&amp;quot;;&lt;br /&gt;
     long long res = 0;&lt;br /&gt;
     for (char c : s)&lt;br /&gt;
         res = res * base + DIGITS.find(c);&lt;br /&gt;
     return res;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
| width=&amp;quot;10px&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 string fromDec(long long n, int base) {&lt;br /&gt;
     static const string DIGITS = &amp;quot;0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ&amp;quot;;&lt;br /&gt;
     string res;&lt;br /&gt;
     while (n &amp;gt;= base) {&lt;br /&gt;
         res += DIGITS[n % base];&lt;br /&gt;
         n /= base;&lt;br /&gt;
     }&lt;br /&gt;
     res += DIGITS[n];&lt;br /&gt;
     reverse(res.begin(), res.end());&lt;br /&gt;
     return res;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Римская система счисления ==&lt;br /&gt;
&lt;br /&gt;
{| width=&amp;quot;100%&amp;quot;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 string toRoman(int n) {&lt;br /&gt;
     static const vector&amp;lt;pair&amp;lt;string, int&amp;gt;&amp;gt; CODES = {&lt;br /&gt;
         {&amp;quot;M&amp;quot;, 1000}, {&amp;quot;CM&amp;quot;, 900}, {&amp;quot;D&amp;quot;, 500}, {&amp;quot;CD&amp;quot;, 400},&lt;br /&gt;
         {&amp;quot;C&amp;quot;, 100}, {&amp;quot;XC&amp;quot;, 90}, {&amp;quot;L&amp;quot;, 50}, {&amp;quot;XL&amp;quot;, 40},&lt;br /&gt;
         {&amp;quot;X&amp;quot;, 10}, {&amp;quot;IX&amp;quot;, 9}, {&amp;quot;V&amp;quot;, 5}, {&amp;quot;IV&amp;quot;, 4}, {&amp;quot;I&amp;quot;, 1}&lt;br /&gt;
     };&lt;br /&gt;
     &lt;br /&gt;
     string res;&lt;br /&gt;
     for (auto &amp;amp;[code, value] : CODES) {&lt;br /&gt;
         while (n &amp;gt;= value) {&lt;br /&gt;
             n -= value;&lt;br /&gt;
             res += code;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
     return res;        &lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
| width=&amp;quot;10px&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 int fromRoman(string &amp;amp;s) {&lt;br /&gt;
     static const vector&amp;lt;pair&amp;lt;string, int&amp;gt;&amp;gt; CODES = {&lt;br /&gt;
         {&amp;quot;M&amp;quot;, 1000}, {&amp;quot;CM&amp;quot;, 900}, {&amp;quot;D&amp;quot;, 500}, {&amp;quot;CD&amp;quot;, 400},&lt;br /&gt;
         {&amp;quot;C&amp;quot;, 100}, {&amp;quot;XC&amp;quot;, 90}, {&amp;quot;L&amp;quot;, 50}, {&amp;quot;XL&amp;quot;, 40},&lt;br /&gt;
         {&amp;quot;X&amp;quot;, 10}, {&amp;quot;IX&amp;quot;, 9}, {&amp;quot;V&amp;quot;, 5}, {&amp;quot;IV&amp;quot;, 4}, {&amp;quot;I&amp;quot;, 1}&lt;br /&gt;
     };&lt;br /&gt;
     &lt;br /&gt;
     int pos = 0, res = 0;&lt;br /&gt;
     for (auto &amp;amp;[code, value] : CODES) {&lt;br /&gt;
         while (s.substr(pos, code.size()) == code) {&lt;br /&gt;
             pos += code.size();&lt;br /&gt;
             res += value;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
     return res;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Задача Иосифа ==&lt;br /&gt;
&lt;br /&gt;
 int size, step;&lt;br /&gt;
 cin &amp;gt;&amp;gt; size &amp;gt;&amp;gt; step;&lt;br /&gt;
 &lt;br /&gt;
 vector&amp;lt;int&amp;gt; a(size);&lt;br /&gt;
 iota(a.begin(), a.end(), 1);&lt;br /&gt;
 &lt;br /&gt;
 int pos = 0;&lt;br /&gt;
 while (!a.empty()) {&lt;br /&gt;
     pos = (pos + step - 1) % a.size();&lt;br /&gt;
     cout &amp;lt;&amp;lt; a[pos] &amp;lt;&amp;lt; &amp;quot; &amp;quot;;&lt;br /&gt;
     a.erase(a.begin() + pos);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Даты ==&lt;br /&gt;
&lt;br /&gt;
 struct Date {&lt;br /&gt;
     int day, month, year, dayOfWeek;&lt;br /&gt;
 &lt;br /&gt;
     bool isLeap(int year) {&lt;br /&gt;
         return year % 400 == 0 || year % 100 &amp;amp;&amp;amp; year % 4 == 0;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     int daysInMonth(int month, int year) {&lt;br /&gt;
         vector&amp;lt;int&amp;gt; days = { 0, 31, 28 + isLeap(year), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };&lt;br /&gt;
         return days[month];&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     Date &amp;amp;operator++(int) {&lt;br /&gt;
         day++;&lt;br /&gt;
         if (day &amp;gt; daysInMonth(month, year)) {&lt;br /&gt;
             day = 1;&lt;br /&gt;
             month++;&lt;br /&gt;
             if (month &amp;gt; 12) {&lt;br /&gt;
                 month = 1;&lt;br /&gt;
                 year++;&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         dayOfWeek = (dayOfWeek + 1) % 7;&lt;br /&gt;
         return *this;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     friend ostream &amp;amp;operator &amp;lt;&amp;lt; (ostream &amp;amp;out, Date &amp;amp;date) {&lt;br /&gt;
         static vector&amp;lt;string&amp;gt; names = {&lt;br /&gt;
             &amp;quot;Monday&amp;quot;, &amp;quot;Tuesday&amp;quot;, &amp;quot;Wednesday&amp;quot;, &amp;quot;Thursday&amp;quot;, &amp;quot;Friday&amp;quot;, &amp;quot;Saturday&amp;quot;, &amp;quot;Sunday&amp;quot;&lt;br /&gt;
         };&lt;br /&gt;
         out &amp;lt;&amp;lt; names[date.dayOfWeek] &amp;lt;&amp;lt; &amp;quot;, &amp;quot;;&lt;br /&gt;
         out &amp;lt;&amp;lt; setw(2) &amp;lt;&amp;lt; setfill(&#039;0&#039;) &amp;lt;&amp;lt; date.day &amp;lt;&amp;lt; &amp;quot;.&amp;quot;;&lt;br /&gt;
         out &amp;lt;&amp;lt; setw(2) &amp;lt;&amp;lt; setfill(&#039;0&#039;) &amp;lt;&amp;lt; date.month;&lt;br /&gt;
         return out;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
== Время ==&lt;br /&gt;
&lt;br /&gt;
{| width=&amp;quot;100%&amp;quot;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 int readTime() {&lt;br /&gt;
     int hours, minutes;&lt;br /&gt;
     char colon;&lt;br /&gt;
     cin &amp;gt;&amp;gt; hours &amp;gt;&amp;gt; colon &amp;gt;&amp;gt; minutes;&lt;br /&gt;
 &lt;br /&gt;
     return hours * 60 + minutes;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
| width=&amp;quot;10px&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 void writeTime(int time) {&lt;br /&gt;
     cout &amp;lt;&amp;lt; setw(2) &amp;lt;&amp;lt; setfill(&#039;0&#039;) &amp;lt;&amp;lt; time / 60 &amp;lt;&amp;lt; &amp;quot;:&amp;quot;;&lt;br /&gt;
     cout &amp;lt;&amp;lt; setw(2) &amp;lt;&amp;lt; setfill(&#039;0&#039;) &amp;lt;&amp;lt; time % 60;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%90%D1%85%D0%BE-%D0%9A%D0%BE%D1%80%D0%B0%D1%81%D0%B8%D0%BA&amp;diff=2907</id>
		<title>Алгоритм Ахо-Корасик</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%90%D1%85%D0%BE-%D0%9A%D0%BE%D1%80%D0%B0%D1%81%D0%B8%D0%BA&amp;diff=2907"/>
		<updated>2023-09-10T13:01:36Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Описание ==&lt;br /&gt;
&lt;br /&gt;
Автомат Ахо-Корасик позволяет находить в тексте вхождения любой строки из заданного набора строк.&lt;br /&gt;
&lt;br /&gt;
Автомат строится на основе бора. Каждая вершина в боре соответствует некоторой строке. Терминальные вершины соответствуют строкам из исходного набора.&lt;br /&gt;
&lt;br /&gt;
Суффиксная ссылка из вершины, соответствующей строке s, ведёт в вершину, соответствующую длиннейшему из имеющихся в боре суффиксов строки s.&amp;lt;br&amp;gt;&lt;br /&gt;
Суть суффиксных ссылок та же, что и ячеек префикс-функции — они определяют, в какое состояние (в какую вершину) мы переходим, если очередной символ текста не совпал с ожидаемым (в данном случае ожидаемыми будут символы, имеющиеся в next у соответствующей вершины бора).&lt;br /&gt;
&lt;br /&gt;
Чтобы за O(1) определять, в какую вершину мы должны перейти, если были в вершине v и встретили символ c, дополнительно вычисляются автоматные ссылки: v-&amp;gt;autLink[c].&lt;br /&gt;
&lt;br /&gt;
Если мы будем просматривать вершины бора в порядке обхода BFSом, то суффиксные и автоматные ссылки можно вычислять так:&lt;br /&gt;
* Суффиксная ссылка из вершины to при просмотре ребра v → (c, to): Если v — корень, то to-&amp;gt;sufLink = v, иначе to-&amp;gt;sufLink = v-&amp;gt;sufLink-&amp;gt;autLink[c].&lt;br /&gt;
* Автоматные ссылки из вершины v: Перебираем все символы алфавита. Если символ c есть в v-&amp;gt;next, то v-&amp;gt;autLink[c] = &amp;amp;v-&amp;gt;next[c], иначе, если v — корень, то v-&amp;gt;autLink[c] = v, иначе v-&amp;gt;autLink[c] = v-&amp;gt;sufLink-&amp;gt;autLink[c].&lt;br /&gt;
&lt;br /&gt;
Чтобы не пропустить вхождения строк, являющиеся суффиксами других строк (набор строк — ABA и B, текст — AB, хотим не пропустить вхождение B), нужно помечать вершину v как терминальную, если терминальной была вершина v-&amp;gt;sufLink.&lt;br /&gt;
&lt;br /&gt;
== Определение позиций вхождений каждой строки ==&lt;br /&gt;
Если нужно не просто проверить, содержал ли текст хотя бы одну строку из набора, а найти позиции вхождений каждой строки, вносятся следующие изменения:&lt;br /&gt;
* Вместо bool isTerminal используются vector&amp;lt;int&amp;gt; termIndexes и Vertex *termLink;&lt;br /&gt;
* termIndexes — массив номеров исходных строк, заканчивающихся в данной вершине (если в наборе не могло быть одинаковых строк, то достаточно одного номера);&lt;br /&gt;
* termLink — ссылка на ближайшую вершину, отличную от данной, у которой isTerminal был бы равен true (то есть эта вершина либо сама является концом какой-то из исходных строк, либо из неё по суффиксным ссылкам достижим такой конец);&lt;br /&gt;
* Признаком того, что найдено вхождение какой-то из исходных строк, раньше было isTerminal == true у текущей вершины. Сейчас это будет либо !termIndexes.empty(), либо termLink != nullptr;&lt;br /&gt;
* Когда мы обнаруживаем вершину с вхождением, от неё нужно переходить по termLink, пока такой переход возможен, и добавлять в ответ termIndexes всех посещённых вершин.&lt;br /&gt;
&lt;br /&gt;
== Оптимизации ==&lt;br /&gt;
* Если не важен порядок символов, тип next и autLink можно заменить на unordered_map;&lt;br /&gt;
* Тип autLink можно заменить на vector или array;&lt;br /&gt;
* Новые вершины можно складывать в отдельный vector или list, тогда тип next можно будет заменить на map&amp;lt;char, int&amp;gt; (если вершины хранятся в vector) или map&amp;lt;char, Vertex *&amp;gt; (если вершины хранятся в list);&lt;br /&gt;
* Можно объединить next и autLink.&lt;br /&gt;
&lt;br /&gt;
== Код ==&lt;br /&gt;
{| width=&amp;quot;100%&amp;quot;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 class Trie {&lt;br /&gt;
     struct Vertex {&lt;br /&gt;
         bool isTerminal = 0;&lt;br /&gt;
         map&amp;lt;char, Vertex&amp;gt; next;&lt;br /&gt;
         Vertex *sufLink = 0;&lt;br /&gt;
         map&amp;lt;char, Vertex *&amp;gt; autLink;&lt;br /&gt;
     } root;&lt;br /&gt;
 &lt;br /&gt;
 public:&lt;br /&gt;
     void insert(const string &amp;amp;s) {&lt;br /&gt;
         Vertex *v = &amp;amp;root;&lt;br /&gt;
         for (char c : s)&lt;br /&gt;
             v = &amp;amp;v-&amp;gt;next[c];&lt;br /&gt;
         v-&amp;gt;isTerminal = 1;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     void build(int alphabetSize) {&lt;br /&gt;
         queue&amp;lt;Vertex *&amp;gt; q;&lt;br /&gt;
         root.sufLink = &amp;amp;root;&lt;br /&gt;
         q.push(&amp;amp;root);&lt;br /&gt;
 &lt;br /&gt;
         while (!q.empty()) {&lt;br /&gt;
             Vertex *v = q.front();&lt;br /&gt;
             q.pop();&lt;br /&gt;
 &lt;br /&gt;
             for (auto &amp;amp;[c, to] : v-&amp;gt;next) {&lt;br /&gt;
                 to.sufLink = (v == &amp;amp;root ? &amp;amp;root : v-&amp;gt;sufLink-&amp;gt;autLink[c]);                &lt;br /&gt;
                 q.push(&amp;amp;to);&lt;br /&gt;
             }&lt;br /&gt;
 &lt;br /&gt;
             for (char c = &#039;a&#039;; c &amp;lt; &#039;a&#039; + alphabetSize; c++) {&lt;br /&gt;
                 if (v-&amp;gt;next.count(c))&lt;br /&gt;
                     v-&amp;gt;autLink[c] = &amp;amp;v-&amp;gt;next[c];&lt;br /&gt;
                 else&lt;br /&gt;
                     v-&amp;gt;autLink[c] = (v == &amp;amp;root ? &amp;amp;root : v-&amp;gt;sufLink-&amp;gt;autLink[c]);&lt;br /&gt;
             }&lt;br /&gt;
 &lt;br /&gt;
             v-&amp;gt;isTerminal |= v-&amp;gt;sufLink-&amp;gt;isTerminal;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     bool check(const string &amp;amp;s) {&lt;br /&gt;
         Vertex *v = &amp;amp;root;&lt;br /&gt;
         for (char c : s) {&lt;br /&gt;
             v = v-&amp;gt;autLink[c];&lt;br /&gt;
             if (v-&amp;gt;isTerminal)&lt;br /&gt;
                 return 1;&lt;br /&gt;
         }&lt;br /&gt;
         return 0;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
| width=&amp;quot;10px&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 class Trie {&lt;br /&gt;
     struct Vertex {&lt;br /&gt;
         vector&amp;lt;int&amp;gt; termIndexes;&lt;br /&gt;
         Vertex *termLink = 0;&lt;br /&gt;
         unordered_map&amp;lt;char, Vertex&amp;gt; next;&lt;br /&gt;
         Vertex *sufLink = 0;&lt;br /&gt;
         unordered_map&amp;lt;char, Vertex *&amp;gt; autLink;&lt;br /&gt;
     } root;&lt;br /&gt;
     int stringCount = 0;&lt;br /&gt;
 &lt;br /&gt;
 public:&lt;br /&gt;
     void insert(const string &amp;amp;s) {&lt;br /&gt;
         Vertex *v = &amp;amp;root;&lt;br /&gt;
         for (char c : s)&lt;br /&gt;
             v = &amp;amp;v-&amp;gt;next[c];&lt;br /&gt;
         v-&amp;gt;termIndexes.push_back(stringCount++);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     void build(int alphabetSize) {&lt;br /&gt;
         queue&amp;lt;Vertex *&amp;gt; q;&lt;br /&gt;
         root.sufLink = &amp;amp;root;&lt;br /&gt;
         q.push(&amp;amp;root);&lt;br /&gt;
 &lt;br /&gt;
         while (!q.empty()) {&lt;br /&gt;
             Vertex *v = q.front();&lt;br /&gt;
             q.pop();&lt;br /&gt;
 &lt;br /&gt;
             for (auto &amp;amp;[c, to] : v-&amp;gt;next) {&lt;br /&gt;
                 to.sufLink = (v == &amp;amp;root ? &amp;amp;root : v-&amp;gt;sufLink-&amp;gt;autLink[c]);&lt;br /&gt;
                 q.push(&amp;amp;to);&lt;br /&gt;
             }&lt;br /&gt;
 &lt;br /&gt;
             for (char c = &#039;a&#039;; c &amp;lt; &#039;a&#039; + alphabetSize; c++) {&lt;br /&gt;
                 if (v-&amp;gt;next.count(c))&lt;br /&gt;
                     v-&amp;gt;autLink[c] = &amp;amp;v-&amp;gt;next[c];&lt;br /&gt;
                 else&lt;br /&gt;
                     v-&amp;gt;autLink[c] = (v == &amp;amp;root ? &amp;amp;root : v-&amp;gt;sufLink-&amp;gt;autLink[c]);&lt;br /&gt;
             }&lt;br /&gt;
 &lt;br /&gt;
             v-&amp;gt;termLink = (!v-&amp;gt;sufLink-&amp;gt;termIndexes.empty() ? v-&amp;gt;sufLink : v-&amp;gt;sufLink-&amp;gt;termLink);&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; check(const string &amp;amp;s) {&lt;br /&gt;
         vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; res(stringCount);&lt;br /&gt;
 &lt;br /&gt;
         Vertex *v = &amp;amp;root;&lt;br /&gt;
         for (int i = 0; i &amp;lt; s.size(); i++) {&lt;br /&gt;
             v = v-&amp;gt;autLink[s[i]];&lt;br /&gt;
 &lt;br /&gt;
             if (!v-&amp;gt;termIndexes.empty() || v-&amp;gt;termLink)&lt;br /&gt;
                 for (Vertex *term = (!v-&amp;gt;termIndexes.empty() ? v : v-&amp;gt;termLink); term; term = term-&amp;gt;termLink)&lt;br /&gt;
                     for (int termIndex : term-&amp;gt;termIndexes)&lt;br /&gt;
                         res[termIndex].push_back(i);&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         return res;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
Теория:&lt;br /&gt;
* [http://e-maxx.ru/algo/aho_corasick e-maxx.ru — Алгоритм Ахо-Корасик]&lt;br /&gt;
* [http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%90%D1%85%D0%BE-%D0%9A%D0%BE%D1%80%D0%B0%D1%81%D0%B8%D0%BA neerc.ifmo.ru/wiki — Алгоритм Ахо-Корасик]&lt;br /&gt;
* [https://codeforces.com/blog/entry/14854 Codeforces — Алгоритм Ахо-Корасик. Построение]&lt;br /&gt;
* Algorithmica — Алгоритм Ахо-Корасик: [https://algorithmica.org/ru/aho-corasick 1], [https://wiki.algocode.ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%90%D1%85%D0%BE-%D0%9A%D0%BE%D1%80%D0%B0%D1%81%D0%B8%D0%BA 2]&lt;br /&gt;
Код:&lt;br /&gt;
* [https://github.com/indy256/codelibrary/blob/master/cpp/strings/aho-corasick.cpp Codelibrary — aho-corasick.cpp]&lt;br /&gt;
* [https://github.com/ADJA/algos/blob/master/Strings/Aho-Corasick.cpp Algos — Aho-Corasick.cpp]&lt;br /&gt;
&lt;br /&gt;
[[Category: Строки]]&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%90%D1%85%D0%BE-%D0%9A%D0%BE%D1%80%D0%B0%D1%81%D0%B8%D0%BA&amp;diff=2906</id>
		<title>Алгоритм Ахо-Корасик</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%90%D1%85%D0%BE-%D0%9A%D0%BE%D1%80%D0%B0%D1%81%D0%B8%D0%BA&amp;diff=2906"/>
		<updated>2023-09-10T13:00:38Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Описание ==&lt;br /&gt;
&lt;br /&gt;
Автомат Ахо-Корасик позволяет находить в тексте вхождения любой строки из заданного набора строк.&lt;br /&gt;
&lt;br /&gt;
Автомат строится на основе бора. Каждая вершина в боре соответствует некоторой строке. Терминальные вершины соответствуют строкам из исходного набора.&lt;br /&gt;
&lt;br /&gt;
Суффиксная ссылка из вершины, соответствующей строке s, ведёт в вершину, соответствующую длиннейшему из имеющихся в боре суффиксов строки s.&amp;lt;br&amp;gt;&lt;br /&gt;
Суть суффиксных ссылок та же, что и ячеек префикс-функции — они определяют, в какое состояние (в какую вершину) мы переходим, если очередной символ текста не совпал с ожидаемым (в данном случае ожидаемыми будут символы, имеющиеся в next у соответствующей вершины бора).&lt;br /&gt;
&lt;br /&gt;
Чтобы за O(1) определять, в какую вершину мы должны перейти, если были в вершине v и встретили символ c, дополнительно вычисляются автоматные ссылки: v-&amp;gt;autLink[c].&lt;br /&gt;
&lt;br /&gt;
Если мы будем просматривать вершины бора в порядке обхода BFSом, то суффиксные и автоматные ссылки можно вычислять так:&lt;br /&gt;
* Суффиксная ссылка из вершины to при просмотре ребра v → (c, to): Если v — корень, то to-&amp;gt;sufLink = v, иначе to-&amp;gt;sufLink = v-&amp;gt;sufLink-&amp;gt;autLink[c].&lt;br /&gt;
* Автоматные ссылки из вершины v: Перебираем все символы алфавита. Если символ c есть в v-&amp;gt;next, то v-&amp;gt;autLink[c] = &amp;amp;v-&amp;gt;next[c], иначе, если v — корень, то v-&amp;gt;autLink[c] = v, иначе v-&amp;gt;autLink[c] = v-&amp;gt;sufLink-&amp;gt;autLink[c].&lt;br /&gt;
&lt;br /&gt;
Чтобы не пропустить вхождения строк, являющиеся суффиксами других строк (набор строк — ABA и B, текст — AB, хотим не пропустить вхождение B), нужно помечать вершину v как терминальную, если терминальной была вершина v-&amp;gt;sufLink.&lt;br /&gt;
&lt;br /&gt;
== Определение позиций вхождений каждой строки ==&lt;br /&gt;
Если нужно не просто проверить, содержал ли текст хотя бы одну строку из набора, а найти позиции вхождений каждой строки, вносятся следующие изменения:&lt;br /&gt;
* Вместо bool isTerminal используются vector&amp;lt;int&amp;gt; termIndexes и Vertex *termLink;&lt;br /&gt;
* termIndexes — массив номеров исходных строк, заканчивающихся в данной вершине (если в наборе не могло быть одинаковых строк, то достаточно одного номера);&lt;br /&gt;
* termLink — ссылка на ближайшую вершину, отличную от данной, у которой isTerminal был бы равен true (то есть эта вершина либо сама является концом какой-то из исходных строк, либо из неё по суффиксным ссылкам достижим такой конец);&lt;br /&gt;
* Признаком того, что найдено вхождение какой-то из исходных строк, раньше было isTerminal == true у текущей вершины. Сейчас это будет либо !termIndexes.empty(), либо termLink != nullptr;&lt;br /&gt;
* Когда мы обнаруживаем вершину с вхождением, от неё нужно переходить по termLink, пока такой переход возможен, и добавлять в ответ termIndexes всех посещённых вершин.&lt;br /&gt;
&lt;br /&gt;
== Оптимизации ==&lt;br /&gt;
* Если не важен порядок символов, тип next и autLink можно заменить на unordered_map;&lt;br /&gt;
* Тип autLink можно заменить на vector или array;&lt;br /&gt;
* Новые вершины можно складывать в отдельный vector или list, тогда тип next можно будет заменить на map&amp;lt;char, int&amp;gt; (если вершины хранятся в vector) или map&amp;lt;char, Vertex *&amp;gt; (если вершины хранятся в list);&lt;br /&gt;
* Можно объединить next и autLink.&lt;br /&gt;
&lt;br /&gt;
== Код ==&lt;br /&gt;
{| width=&amp;quot;100%&amp;quot;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 class Trie {&lt;br /&gt;
     struct Vertex {&lt;br /&gt;
         bool isTerminal = 0;&lt;br /&gt;
         map&amp;lt;char, Vertex&amp;gt; next;&lt;br /&gt;
         Vertex *sufLink = 0;&lt;br /&gt;
         map&amp;lt;char, Vertex *&amp;gt; autLink;&lt;br /&gt;
     } root;&lt;br /&gt;
 &lt;br /&gt;
 public:&lt;br /&gt;
     void insert(const string &amp;amp;s) {&lt;br /&gt;
         Vertex *v = &amp;amp;root;&lt;br /&gt;
         for (char c : s)&lt;br /&gt;
             v = &amp;amp;v-&amp;gt;next[c];&lt;br /&gt;
         v-&amp;gt;isTerminal = 1;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     void build(int alphabetSize) {&lt;br /&gt;
         queue&amp;lt;Vertex *&amp;gt; q;&lt;br /&gt;
         root.sufLink = &amp;amp;root;&lt;br /&gt;
         q.push(&amp;amp;root);&lt;br /&gt;
 &lt;br /&gt;
         while (!q.empty()) {&lt;br /&gt;
             Vertex *v = q.front();&lt;br /&gt;
             q.pop();&lt;br /&gt;
 &lt;br /&gt;
             for (auto &amp;amp;[c, to] : v-&amp;gt;next) {&lt;br /&gt;
                 to.sufLink = (v == &amp;amp;root ? &amp;amp;root : v-&amp;gt;sufLink-&amp;gt;autLink[c]);                &lt;br /&gt;
                 q.push(&amp;amp;to);&lt;br /&gt;
             }&lt;br /&gt;
 &lt;br /&gt;
             for (char c = &#039;a&#039;; c &amp;lt; &#039;a&#039; + alphabetSize; c++) {&lt;br /&gt;
                 if (v-&amp;gt;next.count(c))&lt;br /&gt;
                     v-&amp;gt;autLink[c] = &amp;amp;v-&amp;gt;next[c];&lt;br /&gt;
                 else&lt;br /&gt;
                     v-&amp;gt;autLink[c] = (v == &amp;amp;root ? &amp;amp;root : v-&amp;gt;sufLink-&amp;gt;autLink[c]);&lt;br /&gt;
             }&lt;br /&gt;
 &lt;br /&gt;
             v-&amp;gt;isTerminal |= v-&amp;gt;sufLink-&amp;gt;isTerminal;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     bool check(const string &amp;amp;s) {&lt;br /&gt;
         Vertex *v = &amp;amp;root;&lt;br /&gt;
         for (char c : s) {&lt;br /&gt;
             v = v-&amp;gt;autLink[c];&lt;br /&gt;
             if (v-&amp;gt;isTerminal)&lt;br /&gt;
                 return 1;&lt;br /&gt;
         }&lt;br /&gt;
         return 0;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
| width=&amp;quot;10px&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 class Trie {&lt;br /&gt;
     struct Vertex {&lt;br /&gt;
         vector&amp;lt;int&amp;gt; termIndexes;&lt;br /&gt;
         Vertex *termLink = 0;&lt;br /&gt;
         map&amp;lt;char, Vertex&amp;gt; next;&lt;br /&gt;
         Vertex *sufLink = 0;&lt;br /&gt;
         map&amp;lt;char, Vertex *&amp;gt; autLink;&lt;br /&gt;
     } root;&lt;br /&gt;
     int stringCount = 0;&lt;br /&gt;
 &lt;br /&gt;
 public:&lt;br /&gt;
     void insert(const string &amp;amp;s) {&lt;br /&gt;
         Vertex *v = &amp;amp;root;&lt;br /&gt;
         for (char c : s)&lt;br /&gt;
             v = &amp;amp;v-&amp;gt;next[c];&lt;br /&gt;
         v-&amp;gt;termIndexes.push_back(stringCount++);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     void build(int alphabetSize) {&lt;br /&gt;
         queue&amp;lt;Vertex *&amp;gt; q;&lt;br /&gt;
         root.sufLink = &amp;amp;root;&lt;br /&gt;
         q.push(&amp;amp;root);&lt;br /&gt;
 &lt;br /&gt;
         while (!q.empty()) {&lt;br /&gt;
             Vertex *v = q.front();&lt;br /&gt;
             q.pop();&lt;br /&gt;
 &lt;br /&gt;
             for (auto &amp;amp;[c, to] : v-&amp;gt;next) {&lt;br /&gt;
                 to.sufLink = (v == &amp;amp;root ? &amp;amp;root : v-&amp;gt;sufLink-&amp;gt;autLink[c]);&lt;br /&gt;
                 q.push(&amp;amp;to);&lt;br /&gt;
             }&lt;br /&gt;
 &lt;br /&gt;
             for (char c = &#039;a&#039;; c &amp;lt; &#039;a&#039; + alphabetSize; c++) {&lt;br /&gt;
                 if (v-&amp;gt;next.count(c))&lt;br /&gt;
                     v-&amp;gt;autLink[c] = &amp;amp;v-&amp;gt;next[c];&lt;br /&gt;
                 else&lt;br /&gt;
                     v-&amp;gt;autLink[c] = (v == &amp;amp;root ? &amp;amp;root : v-&amp;gt;sufLink-&amp;gt;autLink[c]);&lt;br /&gt;
             }&lt;br /&gt;
 &lt;br /&gt;
             v-&amp;gt;termLink = (!v-&amp;gt;sufLink-&amp;gt;termIndexes.empty() ? v-&amp;gt;sufLink : v-&amp;gt;sufLink-&amp;gt;termLink);&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; check(const string &amp;amp;s) {&lt;br /&gt;
         vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; res(stringCount);&lt;br /&gt;
 &lt;br /&gt;
         Vertex *v = &amp;amp;root;&lt;br /&gt;
         for (int i = 0; i &amp;lt; s.size(); i++) {&lt;br /&gt;
             v = v-&amp;gt;autLink[s[i]];&lt;br /&gt;
 &lt;br /&gt;
             if (!v-&amp;gt;termIndexes.empty() || v-&amp;gt;termLink)&lt;br /&gt;
                 for (Vertex *term = (!v-&amp;gt;termIndexes.empty() ? v : v-&amp;gt;termLink); term; term = term-&amp;gt;termLink)&lt;br /&gt;
                     for (int termIndex : term-&amp;gt;termIndexes)&lt;br /&gt;
                         res[termIndex].push_back(i);&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         return res;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
Теория:&lt;br /&gt;
* [http://e-maxx.ru/algo/aho_corasick e-maxx.ru — Алгоритм Ахо-Корасик]&lt;br /&gt;
* [http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%90%D1%85%D0%BE-%D0%9A%D0%BE%D1%80%D0%B0%D1%81%D0%B8%D0%BA neerc.ifmo.ru/wiki — Алгоритм Ахо-Корасик]&lt;br /&gt;
* [https://codeforces.com/blog/entry/14854 Codeforces — Алгоритм Ахо-Корасик. Построение]&lt;br /&gt;
* Algorithmica — Алгоритм Ахо-Корасик: [https://algorithmica.org/ru/aho-corasick 1], [https://wiki.algocode.ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%90%D1%85%D0%BE-%D0%9A%D0%BE%D1%80%D0%B0%D1%81%D0%B8%D0%BA 2]&lt;br /&gt;
Код:&lt;br /&gt;
* [https://github.com/indy256/codelibrary/blob/master/cpp/strings/aho-corasick.cpp Codelibrary — aho-corasick.cpp]&lt;br /&gt;
* [https://github.com/ADJA/algos/blob/master/Strings/Aho-Corasick.cpp Algos — Aho-Corasick.cpp]&lt;br /&gt;
&lt;br /&gt;
[[Category: Строки]]&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%90%D1%85%D0%BE-%D0%9A%D0%BE%D1%80%D0%B0%D1%81%D0%B8%D0%BA&amp;diff=2905</id>
		<title>Алгоритм Ахо-Корасик</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%90%D1%85%D0%BE-%D0%9A%D0%BE%D1%80%D0%B0%D1%81%D0%B8%D0%BA&amp;diff=2905"/>
		<updated>2023-09-07T01:03:07Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Автомат Ахо-Корасик позволяет находить в тексте вхождения любой строки из заданного набора строк.&lt;br /&gt;
&lt;br /&gt;
Автомат строится на основе бора. Каждая вершина в боре соответствует некоторой строке. Терминальные вершины соответствуют строкам из исходного набора.&lt;br /&gt;
&lt;br /&gt;
Суффиксная ссылка из вершины, соответствующей строке s, ведёт в вершину, соответствующую длиннейшему из имеющихся в боре суффиксов строки s.&amp;lt;br&amp;gt;&lt;br /&gt;
Суть суффиксных ссылок та же, что и ячеек префикс-функции — они определяют, в какое состояние (в какую вершину) мы переходим, если очередной символ текста не совпал с ожидаемым (в данном случае ожидаемыми будут символы, имеющиеся в next у соответствующей вершины бора).&lt;br /&gt;
&lt;br /&gt;
Чтобы за O(1) определять, в какую вершину мы должны перейти, если были в вершине v и встретили символ c, дополнительно вычисляются автоматные ссылки: v-&amp;gt;autLink[c].&lt;br /&gt;
&lt;br /&gt;
Если мы будем просматривать вершины бора в порядке обхода BFSом, то суффиксные и автоматные ссылки можно вычислять так:&lt;br /&gt;
* Суффиксная ссылка из вершины to при просмотре ребра v → (c, to): Если v — корень, то to-&amp;gt;sufLink = v, иначе to-&amp;gt;sufLink = v-&amp;gt;sufLink-&amp;gt;autLink[c].&lt;br /&gt;
* Автоматные ссылки из вершины v: Перебираем все символы алфавита. Если символ c есть в v-&amp;gt;next, то v-&amp;gt;autLink[c] = &amp;amp;v-&amp;gt;next[c], иначе, если v — корень, то v-&amp;gt;autLink[c] = v, иначе v-&amp;gt;autLink[c] = v-&amp;gt;sufLink-&amp;gt;autLink[c].&lt;br /&gt;
&lt;br /&gt;
Чтобы не пропустить вхождения строк, являющиеся суффиксами других строк (набор строк — ABA и B, текст — AB, хотим не пропустить вхождение B), нужно помечать вершину v как терминальную, если терминальной была вершина v-&amp;gt;sufLink.&lt;br /&gt;
&lt;br /&gt;
 class Trie {&lt;br /&gt;
     struct Vertex {&lt;br /&gt;
         bool isTerminal = false;&lt;br /&gt;
         map&amp;lt;char, Vertex&amp;gt; next;&lt;br /&gt;
         Vertex *sufLink = nullptr;&lt;br /&gt;
         map&amp;lt;char, Vertex *&amp;gt; autLink;&lt;br /&gt;
     } root;&lt;br /&gt;
 &lt;br /&gt;
 public:&lt;br /&gt;
     void add(const string &amp;amp;s) {&lt;br /&gt;
         Vertex *v = &amp;amp;root;&lt;br /&gt;
         for (const char &amp;amp;c : s)&lt;br /&gt;
             v = &amp;amp;v-&amp;gt;next[c];&lt;br /&gt;
         v-&amp;gt;isTerminal = true;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     void build(int alphabetSize) {&lt;br /&gt;
         queue&amp;lt;Vertex *&amp;gt; q;&lt;br /&gt;
         root.sufLink = &amp;amp;root;&lt;br /&gt;
         q.push(&amp;amp;root);&lt;br /&gt;
 &lt;br /&gt;
         while (!q.empty()) {&lt;br /&gt;
             Vertex *v = q.front();&lt;br /&gt;
             q.pop();&lt;br /&gt;
 &lt;br /&gt;
             for (auto &amp;amp;[c, to] : v-&amp;gt;next) {&lt;br /&gt;
                 to.sufLink = (v == &amp;amp;root ? &amp;amp;root : v-&amp;gt;sufLink-&amp;gt;autLink[c]);                &lt;br /&gt;
                 q.push(&amp;amp;to);&lt;br /&gt;
             }&lt;br /&gt;
 &lt;br /&gt;
             for (char c = &#039;a&#039;; c &amp;lt; &#039;a&#039; + alphabetSize; c++) {&lt;br /&gt;
                 if (v-&amp;gt;next.count(c))&lt;br /&gt;
                     v-&amp;gt;autLink[c] = &amp;amp;v-&amp;gt;next[c];&lt;br /&gt;
                 else&lt;br /&gt;
                     v-&amp;gt;autLink[c] = (v == &amp;amp;root ? &amp;amp;root : v-&amp;gt;sufLink-&amp;gt;autLink[c]);&lt;br /&gt;
             }&lt;br /&gt;
 &lt;br /&gt;
             v-&amp;gt;isTerminal |= v-&amp;gt;sufLink-&amp;gt;isTerminal;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     bool check(const string &amp;amp;s) {&lt;br /&gt;
         Vertex *v = &amp;amp;root;&lt;br /&gt;
         for (const char &amp;amp;c : s) {&lt;br /&gt;
             v = v-&amp;gt;autLink[c];&lt;br /&gt;
             if (v-&amp;gt;isTerminal)&lt;br /&gt;
                 return true;&lt;br /&gt;
         }&lt;br /&gt;
         return false;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
Теория:&lt;br /&gt;
* [http://e-maxx.ru/algo/aho_corasick e-maxx.ru — Алгоритм Ахо-Корасик]&lt;br /&gt;
* [http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%90%D1%85%D0%BE-%D0%9A%D0%BE%D1%80%D0%B0%D1%81%D0%B8%D0%BA neerc.ifmo.ru/wiki — Алгоритм Ахо-Корасик]&lt;br /&gt;
* [https://codeforces.com/blog/entry/14854 Codeforces — Алгоритм Ахо-Корасик. Построение]&lt;br /&gt;
* Algorithmica — Алгоритм Ахо-Корасик: [https://algorithmica.org/ru/aho-corasick 1], [https://wiki.algocode.ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%90%D1%85%D0%BE-%D0%9A%D0%BE%D1%80%D0%B0%D1%81%D0%B8%D0%BA 2]&lt;br /&gt;
Код:&lt;br /&gt;
* [https://github.com/indy256/codelibrary/blob/master/cpp/strings/aho-corasick.cpp Codelibrary — aho-corasick.cpp]&lt;br /&gt;
* [https://github.com/ADJA/algos/blob/master/Strings/Aho-Corasick.cpp Algos — Aho-Corasick.cpp]&lt;br /&gt;
&lt;br /&gt;
[[Category: Строки]]&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%90%D1%85%D0%BE-%D0%9A%D0%BE%D1%80%D0%B0%D1%81%D0%B8%D0%BA&amp;diff=2904</id>
		<title>Алгоритм Ахо-Корасик</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%90%D1%85%D0%BE-%D0%9A%D0%BE%D1%80%D0%B0%D1%81%D0%B8%D0%BA&amp;diff=2904"/>
		<updated>2023-09-07T00:59:57Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Автомат Ахо-Корасик позволяет находить в тексте вхождения любой строки из заданного набора строк.&lt;br /&gt;
&lt;br /&gt;
Автомат строится на основе бора. Каждая вершина в боре соответствует некоторой строке. Терминальные вершины соответствуют строкам из исходного набора.&lt;br /&gt;
&lt;br /&gt;
Суффиксная ссылка из вершины, соответствующей строке s, ведёт в вершину, соответствующую длиннейшему из имеющихся в боре суффиксов строки s.&amp;lt;br&amp;gt;&lt;br /&gt;
Суть суффиксных ссылок та же, что и ячеек префикс-функции — они определяют, в какое состояние (в какую вершину) мы переходим, если очередной символ текста не совпал с ожидаемым (в данном случае ожидаемыми будут символы, имеющиеся в next у соответствующей вершины бора).&lt;br /&gt;
&lt;br /&gt;
Чтобы за O(1) определять, в какую вершину мы должны перейти, если были в вершине v и встретили символ c, дополнительно вычисляются автоматные ссылки: v-&amp;gt;autLink[c].&lt;br /&gt;
&lt;br /&gt;
Если мы будем просматривать вершины бора в порядке обхода BFSом, то суффиксные и автоматные ссылки можно вычислять так:&lt;br /&gt;
* Суффиксная ссылка из вершины to при просмотре ребра v → (c, to): Если v — корень, то to-&amp;gt;sufLink = v, иначе to-&amp;gt;sufLink = v-&amp;gt;sufLink-&amp;gt;autLink[c].&lt;br /&gt;
* Автоматные ссылки из вершины v: Перебираем все символы алфавита. Если символ c есть в v-&amp;gt;next, то v-&amp;gt;autLink[c] = &amp;amp;v-&amp;gt;next[c], иначе, если v — корень, то v-&amp;gt;autLink[c] = v, иначе v-&amp;gt;autLink[c] = v-&amp;gt;sufLink-&amp;gt;autLink[c].&lt;br /&gt;
&lt;br /&gt;
Чтобы не пропустить вхождения строк, являющиеся суффиксами других строк (набор строк — ABA и B, текст — AB, хотим не пропустить вхождение B), нужно помечать вершину v как терминальную, если терминальной была вершина v-&amp;gt;sufLink.&lt;br /&gt;
&lt;br /&gt;
 class Trie {&lt;br /&gt;
     struct Vertex {&lt;br /&gt;
         bool isTerminal = false;&lt;br /&gt;
         map&amp;lt;char, Vertex&amp;gt; next;&lt;br /&gt;
         Vertex *sufLink = nullptr;&lt;br /&gt;
         map&amp;lt;char, Vertex *&amp;gt; autLink;&lt;br /&gt;
     } root;&lt;br /&gt;
 &lt;br /&gt;
 public:&lt;br /&gt;
     void add(const string &amp;amp;s) {&lt;br /&gt;
         Vertex *v = &amp;amp;root;&lt;br /&gt;
         for (const char &amp;amp;c : s)&lt;br /&gt;
             v = &amp;amp;v-&amp;gt;next[c];&lt;br /&gt;
         v-&amp;gt;isTerminal = true;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     void build(int alphabetSize) {&lt;br /&gt;
         queue&amp;lt;Vertex *&amp;gt; q;&lt;br /&gt;
         q.push(&amp;amp;root);&lt;br /&gt;
 &lt;br /&gt;
         while (!q.empty()) {&lt;br /&gt;
             Vertex *v = q.front();&lt;br /&gt;
             q.pop();&lt;br /&gt;
 &lt;br /&gt;
             for (auto &amp;amp;[c, to] : v-&amp;gt;next) {&lt;br /&gt;
                 to.sufLink = (v == &amp;amp;root ? &amp;amp;root : v-&amp;gt;sufLink-&amp;gt;autLink[c]);                &lt;br /&gt;
                 q.push(&amp;amp;to);&lt;br /&gt;
             }&lt;br /&gt;
 &lt;br /&gt;
             for (char c = &#039;a&#039;; c &amp;lt; &#039;a&#039; + alphabetSize; c++) {&lt;br /&gt;
                 if (v-&amp;gt;next.count(c))&lt;br /&gt;
                     v-&amp;gt;autLink[c] = &amp;amp;v-&amp;gt;next[c];&lt;br /&gt;
                 else&lt;br /&gt;
                     v-&amp;gt;autLink[c] = (v == &amp;amp;root ? &amp;amp;root : v-&amp;gt;sufLink-&amp;gt;autLink[c]);&lt;br /&gt;
             }&lt;br /&gt;
 &lt;br /&gt;
             v-&amp;gt;isTerminal |= v-&amp;gt;sufLink-&amp;gt;isTerminal;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     bool check(const string &amp;amp;s) {&lt;br /&gt;
         Vertex *v = &amp;amp;root;&lt;br /&gt;
         for (const char &amp;amp;c : s) {&lt;br /&gt;
             v = v-&amp;gt;autLink[c];&lt;br /&gt;
             if (v-&amp;gt;isTerminal)&lt;br /&gt;
                 return true;&lt;br /&gt;
         }&lt;br /&gt;
         return false;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
Теория:&lt;br /&gt;
* [http://e-maxx.ru/algo/aho_corasick e-maxx.ru — Алгоритм Ахо-Корасик]&lt;br /&gt;
* [http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%90%D1%85%D0%BE-%D0%9A%D0%BE%D1%80%D0%B0%D1%81%D0%B8%D0%BA neerc.ifmo.ru/wiki — Алгоритм Ахо-Корасик]&lt;br /&gt;
* [https://codeforces.com/blog/entry/14854 Codeforces — Алгоритм Ахо-Корасик. Построение]&lt;br /&gt;
* Algorithmica — Алгоритм Ахо-Корасик: [https://algorithmica.org/ru/aho-corasick 1], [https://wiki.algocode.ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%90%D1%85%D0%BE-%D0%9A%D0%BE%D1%80%D0%B0%D1%81%D0%B8%D0%BA 2]&lt;br /&gt;
Код:&lt;br /&gt;
* [https://github.com/indy256/codelibrary/blob/master/cpp/strings/aho-corasick.cpp Codelibrary — aho-corasick.cpp]&lt;br /&gt;
* [https://github.com/ADJA/algos/blob/master/Strings/Aho-Corasick.cpp Algos — Aho-Corasick.cpp]&lt;br /&gt;
&lt;br /&gt;
[[Category: Строки]]&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=%D0%A7%D0%B0%D1%81%D1%82%D0%BE_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D1%83%D0%B5%D0%BC%D1%8B%D0%B5_%D1%84%D1%80%D0%B0%D0%B3%D0%BC%D0%B5%D0%BD%D1%82%D1%8B&amp;diff=2903</id>
		<title>Часто используемые фрагменты</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=%D0%A7%D0%B0%D1%81%D1%82%D0%BE_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D1%83%D0%B5%D0%BC%D1%8B%D0%B5_%D1%84%D1%80%D0%B0%D0%B3%D0%BC%D0%B5%D0%BD%D1%82%D1%8B&amp;diff=2903"/>
		<updated>2023-09-01T10:36:02Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Split и join ==&lt;br /&gt;
&lt;br /&gt;
{| width=&amp;quot;100%&amp;quot;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 vector&amp;lt;string&amp;gt; split(string &amp;amp;line) {&lt;br /&gt;
     vector&amp;lt;string&amp;gt; words;&lt;br /&gt;
     string word;&lt;br /&gt;
     for (char c : line) {&lt;br /&gt;
         if (c != &#039; &#039;) {&lt;br /&gt;
             word += c;&lt;br /&gt;
         } else if (!word.empty()) {&lt;br /&gt;
             words.push_back(word);&lt;br /&gt;
             word.clear();&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
     if (!word.empty())&lt;br /&gt;
         words.push_back(word);&lt;br /&gt;
     return words;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
| width=&amp;quot;10px&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 string join(vector&amp;lt;string&amp;gt; &amp;amp;words) {&lt;br /&gt;
     string line;&lt;br /&gt;
     for (int i = 0; i &amp;lt; words.size(); i++)&lt;br /&gt;
         line += words[i] + (i + 1 &amp;lt; words.size() ? &amp;quot; &amp;quot; : &amp;quot;&amp;quot;);&lt;br /&gt;
     return line;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| width=&amp;quot;100%&amp;quot;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 vector&amp;lt;string&amp;gt; split(string &amp;amp;line, const string &amp;amp;separators) {&lt;br /&gt;
     vector&amp;lt;string&amp;gt; words;&lt;br /&gt;
     string word;&lt;br /&gt;
     for (char c : line) {&lt;br /&gt;
         if (separators.find(c) == -1) {&lt;br /&gt;
             word += c;&lt;br /&gt;
         } else if (!word.empty()) {&lt;br /&gt;
             words.push_back(word);&lt;br /&gt;
             word.clear();&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
     if (!word.empty())&lt;br /&gt;
         words.push_back(word);&lt;br /&gt;
     return words;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
| width=&amp;quot;10px&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 string join(vector&amp;lt;string&amp;gt; &amp;amp;words, const string &amp;amp;separator) {&lt;br /&gt;
     string line;&lt;br /&gt;
     for (int i = 0; i &amp;lt; words.size(); i++)&lt;br /&gt;
         line += words[i] + (i + 1 &amp;lt; words.size() ? separator : &amp;quot;&amp;quot;);&lt;br /&gt;
     return line;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Сумма цифр ==&lt;br /&gt;
{| width=&amp;quot;100%&amp;quot;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 int digitSum(int n) {&lt;br /&gt;
     int sum = 0;&lt;br /&gt;
     while (n) {&lt;br /&gt;
         sum += n % 10;&lt;br /&gt;
         n /= 10;&lt;br /&gt;
     }&lt;br /&gt;
     return sum;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
| width=&amp;quot;10px&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 int ones(int n) {&lt;br /&gt;
     int res = 0;&lt;br /&gt;
     while (n) {&lt;br /&gt;
         res++;&lt;br /&gt;
         n &amp;amp;= n - 1;&lt;br /&gt;
     }&lt;br /&gt;
     return res;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Перевод систем счисления ==&lt;br /&gt;
&lt;br /&gt;
{| width=&amp;quot;100%&amp;quot;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 long long toDec(string s, int base) {&lt;br /&gt;
     static const string DIGITS = &amp;quot;0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ&amp;quot;;&lt;br /&gt;
     long long res = 0;&lt;br /&gt;
     for (char c : s)&lt;br /&gt;
         res = res * base + DIGITS.find(c);&lt;br /&gt;
     return res;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
| width=&amp;quot;10px&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 string fromDec(long long n, int base) {&lt;br /&gt;
     static const string DIGITS = &amp;quot;0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ&amp;quot;;&lt;br /&gt;
     string res;&lt;br /&gt;
     while (n &amp;gt;= base) {&lt;br /&gt;
         res += DIGITS[n % base];&lt;br /&gt;
         n /= base;&lt;br /&gt;
     }&lt;br /&gt;
     res += DIGITS[n];&lt;br /&gt;
     reverse(res.begin(), res.end());&lt;br /&gt;
     return res;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Римская система счисления ==&lt;br /&gt;
&lt;br /&gt;
{| width=&amp;quot;100%&amp;quot;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 string toRoman(int n) {&lt;br /&gt;
     static const vector&amp;lt;pair&amp;lt;string, int&amp;gt;&amp;gt; CODES = {&lt;br /&gt;
         {&amp;quot;M&amp;quot;, 1000}, {&amp;quot;CM&amp;quot;, 900}, {&amp;quot;D&amp;quot;, 500}, {&amp;quot;CD&amp;quot;, 400},&lt;br /&gt;
         {&amp;quot;C&amp;quot;, 100}, {&amp;quot;XC&amp;quot;, 90}, {&amp;quot;L&amp;quot;, 50}, {&amp;quot;XL&amp;quot;, 40},&lt;br /&gt;
         {&amp;quot;X&amp;quot;, 10}, {&amp;quot;IX&amp;quot;, 9}, {&amp;quot;V&amp;quot;, 5}, {&amp;quot;IV&amp;quot;, 4}, {&amp;quot;I&amp;quot;, 1}&lt;br /&gt;
     };&lt;br /&gt;
     &lt;br /&gt;
     string res;&lt;br /&gt;
     for (auto &amp;amp;[code, value] : CODES) {&lt;br /&gt;
         while (n &amp;gt;= value) {&lt;br /&gt;
             n -= value;&lt;br /&gt;
             res += code;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
     return res;        &lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
| width=&amp;quot;10px&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 int fromRoman(string &amp;amp;s) {&lt;br /&gt;
     static const vector&amp;lt;pair&amp;lt;string, int&amp;gt;&amp;gt; CODES = {&lt;br /&gt;
         {&amp;quot;M&amp;quot;, 1000}, {&amp;quot;CM&amp;quot;, 900}, {&amp;quot;D&amp;quot;, 500}, {&amp;quot;CD&amp;quot;, 400},&lt;br /&gt;
         {&amp;quot;C&amp;quot;, 100}, {&amp;quot;XC&amp;quot;, 90}, {&amp;quot;L&amp;quot;, 50}, {&amp;quot;XL&amp;quot;, 40},&lt;br /&gt;
         {&amp;quot;X&amp;quot;, 10}, {&amp;quot;IX&amp;quot;, 9}, {&amp;quot;V&amp;quot;, 5}, {&amp;quot;IV&amp;quot;, 4}, {&amp;quot;I&amp;quot;, 1}&lt;br /&gt;
     };&lt;br /&gt;
     &lt;br /&gt;
     int pos = 0, res = 0;&lt;br /&gt;
     for (auto &amp;amp;[code, value] : CODES) {&lt;br /&gt;
         while (s.substr(pos, code.size()) == code) {&lt;br /&gt;
             pos += code.size();&lt;br /&gt;
             res += value;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
     return res;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Задача Иосифа ==&lt;br /&gt;
&lt;br /&gt;
 int size, step;&lt;br /&gt;
 cin &amp;gt;&amp;gt; size &amp;gt;&amp;gt; step;&lt;br /&gt;
 &lt;br /&gt;
 vector&amp;lt;int&amp;gt; a(size);&lt;br /&gt;
 iota(a.begin(), a.end(), 1);&lt;br /&gt;
 &lt;br /&gt;
 int pos = 0;&lt;br /&gt;
 while (!a.empty()) {&lt;br /&gt;
     pos = (pos + step - 1) % a.size();&lt;br /&gt;
     cout &amp;lt;&amp;lt; a[pos] &amp;lt;&amp;lt; &amp;quot; &amp;quot;;&lt;br /&gt;
     a.erase(a.begin() + pos);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Даты ==&lt;br /&gt;
&lt;br /&gt;
 struct Date {&lt;br /&gt;
     int day, month, year, dayOfWeek;&lt;br /&gt;
 &lt;br /&gt;
     bool isLeap(int year) {&lt;br /&gt;
         return year % 400 == 0 || year % 100 &amp;amp;&amp;amp; year % 4 == 0;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     int daysInMonth(int month, int year) {&lt;br /&gt;
         vector&amp;lt;int&amp;gt; days = { 0, 31, 28 + isLeap(year), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };&lt;br /&gt;
         return days[month];&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     Date &amp;amp;operator++(int) {&lt;br /&gt;
         day++;&lt;br /&gt;
         if (day &amp;gt; daysInMonth(month, year)) {&lt;br /&gt;
             day = 1;&lt;br /&gt;
             month++;&lt;br /&gt;
             if (month &amp;gt; 12) {&lt;br /&gt;
                 month = 1;&lt;br /&gt;
                 year++;&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         dayOfWeek = (dayOfWeek + 1) % 7;&lt;br /&gt;
         return *this;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     friend ostream &amp;amp;operator &amp;lt;&amp;lt; (ostream &amp;amp;out, Date &amp;amp;date) {&lt;br /&gt;
         static vector&amp;lt;string&amp;gt; names = {&lt;br /&gt;
             &amp;quot;Monday&amp;quot;, &amp;quot;Tuesday&amp;quot;, &amp;quot;Wednesday&amp;quot;, &amp;quot;Thursday&amp;quot;, &amp;quot;Friday&amp;quot;, &amp;quot;Saturday&amp;quot;, &amp;quot;Sunday&amp;quot;&lt;br /&gt;
         };&lt;br /&gt;
         out &amp;lt;&amp;lt; names[date.dayOfWeek] &amp;lt;&amp;lt; &amp;quot;, &amp;quot;;&lt;br /&gt;
         out &amp;lt;&amp;lt; setw(2) &amp;lt;&amp;lt; setfill(&#039;0&#039;) &amp;lt;&amp;lt; date.day &amp;lt;&amp;lt; &amp;quot;.&amp;quot;;&lt;br /&gt;
         out &amp;lt;&amp;lt; setw(2) &amp;lt;&amp;lt; setfill(&#039;0&#039;) &amp;lt;&amp;lt; date.month;&lt;br /&gt;
         return out;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
== Время ==&lt;br /&gt;
&lt;br /&gt;
{| width=&amp;quot;100%&amp;quot;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 int readTime() {&lt;br /&gt;
     int hours, minutes;&lt;br /&gt;
     char colon;&lt;br /&gt;
     cin &amp;gt;&amp;gt; hours &amp;gt;&amp;gt; colon &amp;gt;&amp;gt; minutes;&lt;br /&gt;
 &lt;br /&gt;
     return h * 60 + m;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
| width=&amp;quot;10px&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 void writeTime(int time) {&lt;br /&gt;
     cout &amp;lt;&amp;lt; setw(2) &amp;lt;&amp;lt; setfill(&#039;0&#039;) &amp;lt;&amp;lt; time / 60 &amp;lt;&amp;lt; &amp;quot;:&amp;quot;;&lt;br /&gt;
     cout &amp;lt;&amp;lt; setw(2) &amp;lt;&amp;lt; setfill(&#039;0&#039;) &amp;lt;&amp;lt; time % 60;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=%D0%A7%D0%B0%D1%81%D1%82%D0%BE_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D1%83%D0%B5%D0%BC%D1%8B%D0%B5_%D1%84%D1%80%D0%B0%D0%B3%D0%BC%D0%B5%D0%BD%D1%82%D1%8B&amp;diff=2902</id>
		<title>Часто используемые фрагменты</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=%D0%A7%D0%B0%D1%81%D1%82%D0%BE_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D1%83%D0%B5%D0%BC%D1%8B%D0%B5_%D1%84%D1%80%D0%B0%D0%B3%D0%BC%D0%B5%D0%BD%D1%82%D1%8B&amp;diff=2902"/>
		<updated>2023-08-13T00:13:44Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Split и join ==&lt;br /&gt;
&lt;br /&gt;
{| width=&amp;quot;100%&amp;quot;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 vector&amp;lt;string&amp;gt; split(string &amp;amp;line) {&lt;br /&gt;
     vector&amp;lt;string&amp;gt; words;&lt;br /&gt;
     string word;&lt;br /&gt;
     for (char c : line) {&lt;br /&gt;
         if (c != &#039; &#039;) {&lt;br /&gt;
             word += c;&lt;br /&gt;
         } else if (!word.empty()) {&lt;br /&gt;
             words.push_back(word);&lt;br /&gt;
             word.clear();&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
     if (!word.empty())&lt;br /&gt;
         words.push_back(word);&lt;br /&gt;
     return words;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
| width=&amp;quot;10px&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 string join(vector&amp;lt;string&amp;gt; &amp;amp;words) {&lt;br /&gt;
     string line;&lt;br /&gt;
     for (int i = 0; i &amp;lt; words.size(); i++)&lt;br /&gt;
         line += words[i] + (i + 1 &amp;lt; words.size() ? &amp;quot; &amp;quot; : &amp;quot;&amp;quot;);&lt;br /&gt;
     return line;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| width=&amp;quot;100%&amp;quot;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 vector&amp;lt;string&amp;gt; split(string &amp;amp;line, const string &amp;amp;separators) {&lt;br /&gt;
     vector&amp;lt;string&amp;gt; words;&lt;br /&gt;
     string word;&lt;br /&gt;
     for (char c : line) {&lt;br /&gt;
         if (separators.find(c) == -1) {&lt;br /&gt;
             word += c;&lt;br /&gt;
         } else if (!word.empty()) {&lt;br /&gt;
             words.push_back(word);&lt;br /&gt;
             word.clear();&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
     if (!word.empty())&lt;br /&gt;
         words.push_back(word);&lt;br /&gt;
     return words;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
| width=&amp;quot;10px&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 string join(vector&amp;lt;string&amp;gt; &amp;amp;words, const string &amp;amp;separator) {&lt;br /&gt;
     string line;&lt;br /&gt;
     for (int i = 0; i &amp;lt; words.size(); i++)&lt;br /&gt;
         line += words[i] + (i + 1 &amp;lt; words.size() ? separator : &amp;quot;&amp;quot;);&lt;br /&gt;
     return line;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Сумма цифр ==&lt;br /&gt;
&lt;br /&gt;
 int digitSum(int n) {&lt;br /&gt;
     int sum = 0;&lt;br /&gt;
     while (n) {&lt;br /&gt;
         sum += n % 10;&lt;br /&gt;
         n /= 10;&lt;br /&gt;
     }&lt;br /&gt;
     return sum;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Перевод систем счисления ==&lt;br /&gt;
&lt;br /&gt;
{| width=&amp;quot;100%&amp;quot;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 long long toDec(string s, int base) {&lt;br /&gt;
     static const string DIGITS = &amp;quot;0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ&amp;quot;;&lt;br /&gt;
     long long res = 0;&lt;br /&gt;
     for (char c : s)&lt;br /&gt;
         res = res * base + DIGITS.find(c);&lt;br /&gt;
     return res;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
| width=&amp;quot;10px&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 string fromDec(long long n, int base) {&lt;br /&gt;
     static const string DIGITS = &amp;quot;0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ&amp;quot;;&lt;br /&gt;
     string res;&lt;br /&gt;
     while (n &amp;gt;= base) {&lt;br /&gt;
         res += DIGITS[n % base];&lt;br /&gt;
         n /= base;&lt;br /&gt;
     }&lt;br /&gt;
     res += DIGITS[n];&lt;br /&gt;
     reverse(res.begin(), res.end());&lt;br /&gt;
     return res;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Римская система счисления ==&lt;br /&gt;
&lt;br /&gt;
{| width=&amp;quot;100%&amp;quot;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 string toRoman(int n) {&lt;br /&gt;
     static const vector&amp;lt;pair&amp;lt;string, int&amp;gt;&amp;gt; CODES = {&lt;br /&gt;
         {&amp;quot;M&amp;quot;, 1000}, {&amp;quot;CM&amp;quot;, 900}, {&amp;quot;D&amp;quot;, 500}, {&amp;quot;CD&amp;quot;, 400},&lt;br /&gt;
         {&amp;quot;C&amp;quot;, 100}, {&amp;quot;XC&amp;quot;, 90}, {&amp;quot;L&amp;quot;, 50}, {&amp;quot;XL&amp;quot;, 40},&lt;br /&gt;
         {&amp;quot;X&amp;quot;, 10}, {&amp;quot;IX&amp;quot;, 9}, {&amp;quot;V&amp;quot;, 5}, {&amp;quot;IV&amp;quot;, 4}, {&amp;quot;I&amp;quot;, 1}&lt;br /&gt;
     };&lt;br /&gt;
     &lt;br /&gt;
     string res;&lt;br /&gt;
     for (auto &amp;amp;[code, value] : CODES) {&lt;br /&gt;
         while (n &amp;gt;= value) {&lt;br /&gt;
             n -= value;&lt;br /&gt;
             res += code;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
     return res;        &lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
| width=&amp;quot;10px&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| width=50% |&lt;br /&gt;
&lt;br /&gt;
 int fromRoman(string &amp;amp;s) {&lt;br /&gt;
     static const vector&amp;lt;pair&amp;lt;string, int&amp;gt;&amp;gt; CODES = {&lt;br /&gt;
         {&amp;quot;M&amp;quot;, 1000}, {&amp;quot;CM&amp;quot;, 900}, {&amp;quot;D&amp;quot;, 500}, {&amp;quot;CD&amp;quot;, 400},&lt;br /&gt;
         {&amp;quot;C&amp;quot;, 100}, {&amp;quot;XC&amp;quot;, 90}, {&amp;quot;L&amp;quot;, 50}, {&amp;quot;XL&amp;quot;, 40},&lt;br /&gt;
         {&amp;quot;X&amp;quot;, 10}, {&amp;quot;IX&amp;quot;, 9}, {&amp;quot;V&amp;quot;, 5}, {&amp;quot;IV&amp;quot;, 4}, {&amp;quot;I&amp;quot;, 1}&lt;br /&gt;
     };&lt;br /&gt;
     &lt;br /&gt;
     int pos = 0, res = 0;&lt;br /&gt;
     for (auto &amp;amp;[code, value] : CODES) {&lt;br /&gt;
         while (s.substr(pos, code.size()) == code) {&lt;br /&gt;
             pos += code.size();&lt;br /&gt;
             res += value;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
     return res;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Задача Иосифа ==&lt;br /&gt;
&lt;br /&gt;
 int size, step;&lt;br /&gt;
 cin &amp;gt;&amp;gt; size &amp;gt;&amp;gt; step;&lt;br /&gt;
 &lt;br /&gt;
 vector&amp;lt;int&amp;gt; a(size);&lt;br /&gt;
 iota(a.begin(), a.end(), 1);&lt;br /&gt;
 &lt;br /&gt;
 int pos = 0;&lt;br /&gt;
 while (!a.empty()) {&lt;br /&gt;
     pos = (pos + step - 1) % a.size();&lt;br /&gt;
     cout &amp;lt;&amp;lt; a[pos] &amp;lt;&amp;lt; &amp;quot; &amp;quot;;&lt;br /&gt;
     a.erase(a.begin() + pos);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Даты ==&lt;br /&gt;
&lt;br /&gt;
 struct Date {&lt;br /&gt;
     int day, month, year, dayOfWeek;&lt;br /&gt;
 &lt;br /&gt;
     bool isLeap(int year) {&lt;br /&gt;
         return year % 400 == 0 || year % 100 &amp;amp;&amp;amp; year % 4 == 0;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     int daysInMonth(int month, int year) {&lt;br /&gt;
         vector&amp;lt;int&amp;gt; days = { 0, 31, 28 + isLeap(year), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };&lt;br /&gt;
         return days[month];&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     Date &amp;amp;operator++(int) {&lt;br /&gt;
         day++;&lt;br /&gt;
         if (day &amp;gt; daysInMonth(month, year)) {&lt;br /&gt;
             day = 1;&lt;br /&gt;
             month++;&lt;br /&gt;
             if (month &amp;gt; 12) {&lt;br /&gt;
                 month = 1;&lt;br /&gt;
                 year++;&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         dayOfWeek = (dayOfWeek + 1) % 7;&lt;br /&gt;
         return *this;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     friend ostream &amp;amp;operator &amp;lt;&amp;lt; (ostream &amp;amp;out, Date &amp;amp;date) {&lt;br /&gt;
         static vector&amp;lt;string&amp;gt; names = {&lt;br /&gt;
             &amp;quot;Monday&amp;quot;, &amp;quot;Tuesday&amp;quot;, &amp;quot;Wednesday&amp;quot;, &amp;quot;Thursday&amp;quot;, &amp;quot;Friday&amp;quot;, &amp;quot;Saturday&amp;quot;, &amp;quot;Sunday&amp;quot;&lt;br /&gt;
         };&lt;br /&gt;
         out &amp;lt;&amp;lt; names[date.dayOfWeek] &amp;lt;&amp;lt; &amp;quot;, &amp;quot;;&lt;br /&gt;
         out &amp;lt;&amp;lt; setw(2) &amp;lt;&amp;lt; setfill(&#039;0&#039;) &amp;lt;&amp;lt; date.day &amp;lt;&amp;lt; &amp;quot;.&amp;quot;;&lt;br /&gt;
         out &amp;lt;&amp;lt; setw(2) &amp;lt;&amp;lt; setfill(&#039;0&#039;) &amp;lt;&amp;lt; date.month;&lt;br /&gt;
         return out;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
	<entry>
		<id>https://acm.khpnets.info/w39/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A4%D0%BE%D1%80%D0%B4%D0%B0-%D0%A4%D0%B0%D0%BB%D0%BA%D0%B5%D1%80%D1%81%D0%BE%D0%BD%D0%B0&amp;diff=2901</id>
		<title>Алгоритм Форда-Фалкерсона</title>
		<link rel="alternate" type="text/html" href="https://acm.khpnets.info/w39/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A4%D0%BE%D1%80%D0%B4%D0%B0-%D0%A4%D0%B0%D0%BB%D0%BA%D0%B5%D1%80%D1%81%D0%BE%D0%BD%D0%B0&amp;diff=2901"/>
		<updated>2023-07-01T13:23:05Z</updated>

		<summary type="html">&lt;p&gt;Ctrlalt: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt; class Graph {&lt;br /&gt;
     struct Edge {&lt;br /&gt;
         int a, b, capacity, flow = 0;&lt;br /&gt;
 &lt;br /&gt;
         Edge(int a, int b, int capacity) :&lt;br /&gt;
             a(a), b(b), capacity(capacity) {}&lt;br /&gt;
 &lt;br /&gt;
         int other(int v) const {&lt;br /&gt;
             return v == a ? b : a;&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         int capacityTo(int v) const {&lt;br /&gt;
             return v == b ? capacity - flow : flow;&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         void addFlowTo(int v, int deltaFlow) {&lt;br /&gt;
             flow += (v == b ? deltaFlow : -deltaFlow);&lt;br /&gt;
         }&lt;br /&gt;
     };&lt;br /&gt;
 &lt;br /&gt;
     vector&amp;lt;Edge&amp;gt; edges;&lt;br /&gt;
     vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; graph;&lt;br /&gt;
     vector&amp;lt;bool&amp;gt; visited;&lt;br /&gt;
     vector&amp;lt;int&amp;gt; edgeTo;&lt;br /&gt;
 &lt;br /&gt;
     void dfs(int v) {&lt;br /&gt;
         visited[v] = 1;&lt;br /&gt;
         for (int e : graph[v]) {&lt;br /&gt;
             int to = edges[e].other(v);&lt;br /&gt;
             if (!visited[to] &amp;amp;&amp;amp; edges[e].capacityTo(to)) {&lt;br /&gt;
                 edgeTo[to] = e;&lt;br /&gt;
                 dfs(to);&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     bool hasPath(int start, int finish) {&lt;br /&gt;
         visited.assign(visited.size(), 0);&lt;br /&gt;
         dfs(start);&lt;br /&gt;
         return visited[finish];&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     int bottleneckCapacity(int start, int finish) {&lt;br /&gt;
         int bCapacity = 1e9;&lt;br /&gt;
         for (int v = finish; v != start; v = edges[edgeTo[v]].other(v))&lt;br /&gt;
             bCapacity = min(bCapacity, edges[edgeTo[v]].capacityTo(v));&lt;br /&gt;
         return bCapacity;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     void addFlow(int start, int finish, int deltaFlow) {&lt;br /&gt;
         for (int v = finish; v != start; v = edges[edgeTo[v]].other(v))&lt;br /&gt;
             edges[edgeTo[v]].addFlowTo(v, deltaFlow);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
 public:&lt;br /&gt;
     Graph(int vertexCount) :&lt;br /&gt;
         graph(vertexCount), visited(vertexCount), edgeTo(vertexCount) {}&lt;br /&gt;
         &lt;br /&gt;
     void addEdge(int from, int to, int capacity) {&lt;br /&gt;
         edges.push_back(Edge(from, to, capacity));&lt;br /&gt;
         graph[from].push_back(edges.size() - 1);&lt;br /&gt;
         graph[to].push_back(edges.size() - 1);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     long long maxFlow(int start, int finish) {&lt;br /&gt;
         long long flow = 0;&lt;br /&gt;
         while (hasPath(start, finish)) {&lt;br /&gt;
             int deltaFlow = bottleneckCapacity(start, finish);&lt;br /&gt;
             addFlow(start, finish, deltaFlow);&lt;br /&gt;
             flow += deltaFlow;&lt;br /&gt;
         }&lt;br /&gt;
         return flow;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
Теория:&lt;br /&gt;
* [https://algs4.cs.princeton.edu/lectures/keynote/64MaxFlow.pdf algs4.cs.princeton.edu — 6.4 Maximum Flow]&lt;br /&gt;
* [http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A4%D0%BE%D1%80%D0%B4%D0%B0-%D0%A4%D0%B0%D0%BB%D0%BA%D0%B5%D1%80%D1%81%D0%BE%D0%BD%D0%B0,_%D1%80%D0%B5%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F_%D1%81_%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D1%8C%D1%8E_%D0%BF%D0%BE%D0%B8%D1%81%D0%BA%D0%B0_%D0%B2_%D0%B3%D0%BB%D1%83%D0%B1%D0%B8%D0%BD%D1%83 neerc.ifmo.ru/wiki — Алгоритм Форда-Фалкерсона]&lt;br /&gt;
* [http://brilliant.org/wiki/ford-fulkerson-algorithm Brilliant.org — Ford-Fulkerson Algorithm]&lt;br /&gt;
Демонстрация:&lt;br /&gt;
* [https://visualgo.net/en/maxflow VisuAlgo — Network Flow]&lt;br /&gt;
Код:&lt;br /&gt;
* [https://github.com/indy256/codelibrary/blob/master/java/graphs/flows/MaxFlowFordFulkerson.java CodeLibrary — Maximum flow. Ford-Fulkerson alogithm in O(V^2 * FLOW)]&lt;br /&gt;
* [http://github.com/ADJA/algos/blob/master/Graphs/FordFulkerson.cpp Algos &amp;amp;mdash; Ford-Fulkerson maxflow]&lt;br /&gt;
* algs4.cs.princeton.edu/code &amp;amp;mdash; [http://algs4.cs.princeton.edu/code/edu/princeton/cs/algs4/FlowEdge.java.html capacitated edge with flow], [http://algs4.cs.princeton.edu/code/edu/princeton/cs/algs4/FlowNetwork.java.html capacitated network], [http://algs4.cs.princeton.edu/code/edu/princeton/cs/algs4/FordFulkerson.java.html maxflow–mincut] (несмотря на название, используется алгоритм Эдмондса-Карпа)&lt;br /&gt;
Задачи:&lt;br /&gt;
* [http://informatics.mccme.ru/course/view.php?id=6 informatics.mccme.ru &amp;amp;mdash; Курс &amp;amp;laquo;Алгоритмы на графах&amp;amp;raquo; &amp;amp;mdash; часть 9]&lt;br /&gt;
* [[:Категория:Задачи: Максимальный поток|Задачи: Максимальный поток]]&lt;br /&gt;
&lt;br /&gt;
[[Category: Максимальный поток]]&lt;/div&gt;</summary>
		<author><name>Ctrlalt</name></author>
	</entry>
</feed>