26

(6 ответов, оставленных в Problems)

Двоичный поиск: будем искать двоичным поиском искомую длину. Тогда задача сводится к такой: по данной длине (обозначим её через X) проверить, можно ли получить >=N свечей этой длины. Для этого считаем сумму величин Li/X (округленных вниз) - это и есть максимальное количество свечей, которое можно получить с длиной X.

27

(15 ответов, оставленных в Problems)

Ну да, сделать какой-то флаг "величины path_exists_i" посчитаны, и если этот флаг стоит - то ничего для этого узла делать больше не надо. Это и называется "ленивым динамическим программированием" - когда мы вычисляем какие-то величины при первом требовании.

28

(15 ответов, оставленных в Problems)

Ну суффиксный автомат ацикличен, поэтому при подсчёте path_exists_i для текущего состояния мы можем считать, что для всех состояний, в которые есть рёбра из текущего состояния, всё уже посчитано. Поэтому мы можем просто брать ответы для них, а глубже уже не заходить.

29

(15 ответов, оставленных в Problems)

Нет-нет-нет, не надо никаких обратных суффиксных ссылок smile
Мы просто просматриваем рёбра, выходящие из каждого состояния.
Скажем, чтобы найти наличие пути из состояния v = 0, мы просматриваем рёбра, исходящие из состояния v = 0 в другие состояния, и использовать ответы, уже посчитанные для них.

30

(15 ответов, оставленных в Problems)

Ещё раз, мы склеиваем три строки через три разделителя, строим по получившейся строке суффиксный автомат. Далее для каждого состояния автомата обходом в глубину (или динамикой с меморизацией, как угодно называть) мы находим три булевских величины: path_exists_1, path_exists_2, path_exists_3, где path_exists_i означает "из текущей вершины есть путь, заканчивающийся символом, равным разделителю номер i, и при этом не содержащий других разделителей".

После этого ответом на задачу будет просто состояние v, для которого все три величины равны true (точнее, искомая подстрока будет длиннейшим путём из истока в это состояние v).

31

(3 ответов, оставленных в Problems)

wtq4er пишет:

>нужно явно хранить для каждой вершины номер её компоненты связности
не совсем понятно, что представляет из себя "номер компоненты связности", как "компоненты связности" вообще нумеруются. можно второй абзац подробнее объяснить?

Изначально мы считаем, что рёбер нет, т.е. граф состоит просто из N вершин. Каждая вершина - в отдельной компоненте связности, т.е. component[ i ] = i (что означает "вершина номер i находится в компоненте с номером i"). Также для каждой компоненты давайте хранить, какие вершины в неё входят - это некий массив списков vertices, где изначально vertices[ i ] = [ i ] (что означает "i-ая компонента содержит единственную вершину i").

Теперь будем добавлять в граф по одному ребру, в порядке убывания их весов. Пусть текущее ребро - это ребро между вершинами x и y. Пусть cx = component[x], cy = component[y]. Тогда, если cx = cy, то это ребро ни на что не влияет. Если же cx != cy, то надо записать ответ для всех пар вершин вида vertices[cx][ i ] и vertices[cy][ j ]. Затем эти две компоненты cx и cy надо соединить в одну. Для этого мы проходимся по списку vertices[cy] и присваиваем для них component = cx; затем мы в конец списка vertices[cx] добавляем весь список vertices[cy].

32

(2 ответов, оставленных в Feedback)

Исправил.

Спасибо всем за то, что оперативно писали о проблемах.

Вероятно, заражение произошло так: некий вирус попал ко мне на домашнюю машину, украл FTP-пароль и автоматически заразил все JavaScript-скрипты на сайте.

33

(2 ответов, оставленных в Algo)

Всех путей - очень много, точнее, их количество неполиномиально. Поэтому применять здесь bfs/dfs бессмысленно, нужен обычный рекурсивный перебор. Впрочем, его легко получить из алгоритма dfs, убрав использование отметок посещённости вершин (т.е. теперь мы будем пытаться заходить во все вершины, а не только непосещённые).

34

(2 ответов, оставленных в Algo)

NotImplemented пишет:

"Теперь заметим, что поскольку ребро (u, w) появилось в остаточной сети только после выполнения i-ой фазы, то отсюда следует". Откуда следует, что ребро появилось посли i-ой фазы?

Кажется, что иными словами всё это доказательство можно записать так.
Либо путь P содержит те же рёбра, что существовали и на предыдущей фазе, поэтому короче он стать не мог.
Либо путь P содержит какие-то новые рёбра, которые не существовали в остаточной сети на предыдущей фазе (однако учтём, что ребро могло появиться в остаточной сети только в результате пропускания потока вдоль этого ребра в обратном направлении). Рассмотрим первое "новое" ребро (u,w) из пути P; тогда, раз по нему пропустили поток на предыдущей фазе, то level_{i-1}[ u ] > level_{i-1}[ w ]; для вершины u мы можем использовать первую часть доказательства леммы, т.е. level_{i+1}[ u ] >= level_i[ u ]; наконец, в пути P расстояние до вершины w на единицу больше расстояния до u, поэтому level_{i+1}[ w ] > level_{i+1}[ u ]; собирая всё вместе, получаем level_{i+1}[ w ] > level_i[ w ]. Проделывая это же для всех остальных "новых" рёбер пути P, получим в итоге требуемое неравенство для вершины v.

На в самой статье в целом изложение слишком схематично.

Я позже вернусь к этой статье и попробую улучшить непонятные места. Будет хорошо, если вы отпишетесь, что ещё было описано в статье не слишком удачно.

35

(7 ответов, оставленных в Algo)

noktigula пишет:

Спасибо, и еще один вопрос, про суффиксные ссылки.

"суффиксная ссылка  ведёт в такое состояние, которому соответствует наидлиннейший суффикс строки w, находящийся в другом классе endpos-эквивалентности"

Каким образом суффикс строки w может находится в другом классе? Ведь согласно лемме 1, все суффиксы строки должы быть endpos-эквивалентными?

Нет, почему же эквивалентными: ведь суффикс строки w мог встречаться где-то ещё, помимо вхождений непосредственно внутри w. Например, на строке "aa" для w="aa" суффиксная ссылка из него ведёт в состояние, соответствующее строке "a" - потому что endpos("aa")={1}, endpos("a")={0,1}.

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

Искать их можно в том же обходе в глубину, который применяется для поиска точек артикуляции. Надо ввести глобальный стек для рёбер, и при каждом проходе по ребру (в обходе в глубину) добавлять его в этот стек (только не надо добавлять одно и то же ребро дважды: из двух вариантов ориентации надо добавлять только первый), а при обнаружении точки сочленения ("if (fup[to] >= tin[v])") доставать из стека все рёбра вплоть до ребра (v,to) - эти рёбра образуют очередную компоненту вершинной двусвязности. После обхода в глубину из рёбер, оставшихся в стеке, также следует образовать отдельную компоненту.

37

(7 ответов, оставленных в Algo)

noktigula
Имеются в виду именно индексы позиций (во всей статье - индексы нумеруются, начиная с нуля). Для строк s="abcaba" будет endpos("caba")={5}, а, например, endpos("a")={0,3,5}.

38

(11 ответов, оставленных в Problems)

Да уж, позапихивать пришлось так ещё smile

Вот мой код: http://pastebin.com/Gr2EAHwV, даёт 1.40 сек.

Из хаков:

  • Не хранить в дереве уже отсортированные вершины, т.е. на i-ом шаге в дереве хранятся только n-i+1 вершин (у теории - улучшает примерно в два раза).

  • Ручной ввод-вывод, вместо обычных scanf/printf: оперировать только с gets/puts (без этого у меня тоже не проходило, так что достаточно важно).

  • А вот при подсчёте номера вершины и раскручивании предков, как оказалось, вполне можно использовать рекурсию вместо хранения предков в массиве: в данном случае рекурсия почти не сказывается на времени работы.

Интересно отметить, что на SPOJ'е суммарное N по всем тестам около 200 тыс., т.к. получается примерно 0.7 сек на 100 тыс. сложных операций с декартовым деревом: вот такое оно медленное.

39

(11 ответов, оставленных в Problems)

Хм, ну с виду всё нормально. Разве что можно отказаться от динамического выделения памяти в insert(), просто сделав глобальный массив node'ов.

40

(11 ответов, оставленных в Problems)

Забавно, что именно сегодня я тоже сдавал эту задачу на тренировке smile
Не знаю, как там на spoj, но у меня проблем с TL не было. Выложи куда-нибудь код, может, там есть какой-нибудь косяк.

41

(6 ответов, оставленных в Offtopic)

Этот раздел хочется восстановить в каком-нибудь виде, но обычную вики поднимать ради этого не хочется.

42

(3 ответов, оставленных в Problems)

Отсортируем рёбра графа, и будем идти по ним в порядке от самых дорогих к самым дешёвым. Идея такая: на текущем шаге мы рассматриваем только рёбра с весом >= текущего, и мы смотрим, какие компоненты связности получаются из этих рёбер, и внутри каждой такой компоненты связности ответ между всеми парами вершин можно присвоить весу текущего ребра (только, понятно, для тех пар вершин, для которых ответ уже был найден ранее, обновлять его не надо).

Чтобы написать это за нормальную асимптотику, нужно явно хранить для каждой вершины номер её компоненты связности, и при рассмотрении текущего ребра смотреть: если оно соединяет вершины разных компонент, то эти две компоненты сливаются в одну, а ответ обновляется для всех пар вершин вида "(вершина_из_первой_компоненты;вершина_из_второй_компоненты)".

Итого будет решение за O(n^2) плюс предварительная сортировка за O(n^2 log n).

43

(1 ответов, оставленных в Algo)

Их нет и в ближайшее время не планируется, т.к. в интернете описаний сортировок и так хватает, да и практического смысла особого нет (в современных языках сортировка встроена).

44

(15 ответов, оставленных в Problems)

Надо найти такую вершину v, у которой st[v].len максимально, и при этом из этой вершины есть путь, содержащий @ (но не #$), есть путь, содержащий # (но не @$), и есть путь, содержащий, $ (но не @#).

45

(1 ответов, оставленных в Feedback)

matklad пишет:

В http://www.e-maxx.ru/algo/lca_linear написал:

мы должны предпосчитать значения sz для всех возможных длин, которых есть O (log N) штук

А разве этих длин не O(N) штук?

да, была опечатка, спасибо, исправил.

matklad пишет:

В http://www.e-maxx.ru/algo/lca_linear написал:
update:
сейчас сдал задачу про lca с помощью Sparse Table и заметил интересный эффект:

если в T первый индекс это l, а второй i, то препроцесинг работает две секунды, запросы семь секунд и задача не проходит по TL.

А если индексы поменять местами, то препроцессинг - одна секунда, запросы - четыре секунды и задача проходит.

Это логично, так как при пересчёте T[l,i] = min (T[l,i-1], T[l+2^(i-1),i-1]) и при ответе на запрос ans = min (T[l,sz], T[r-2^sz+1,sz]) используются два элемента с одинаковыми i.

А в коде и в статье используется как раз медленный вариант.

Да, действительно почти всегда быстрее оказывается ставить вершину вторым индексом, но 4 года назад я этого не знал smile
Код пока не рискую исправлять, чтобы не посадить баги, исправлю при следующем переписывании статьи.

46

(1 ответов, оставленных в Feedback)

Исправлено, спасибо.

47

(1 ответов, оставленных в Feedback)

Достаточно. Статья очень устарела.

48

(1 ответов, оставленных в Feedback)

Да, вы правы, массив used совсем не обязателен, достаточно лишь сравнения с текущим предком - p.
Исправил код.

49

(1 ответов, оставленных в Feedback)

Да, алгоритм, который последовательно ищет кратчайшие пути - не будет работать, поскольку он предполагает перед каждым поиском кратчайшего пути, что поток минимальной стоимости для текущего потока уже найден. Однако если есть цикл отрицательного веса, то это означает, что уже при нулевом потоке можно добиться отрицательной стоимости, т.е. поиск этого "начального" потока уже не тривиален.

Но есть и другой алгоритм - последовательного поиска циклов отрицательного веса. Он уже работает и в таких случаях, хотя в такой реализации заметно медленнее первого алгоритма. Кстати, его можно ускорить, если искать не просто любой цикл отрицательного веса, а цикл с минимальным средним весом.

50

(32 ответов, оставленных в Problems)

Hack-Yer пишет:

Как написать динамику http://e-maxx.ru/algo/suffix_automata#16?

Количество различных подстрок? Ну прямо по той формуле, что в статье написана: значение динамики для какого-либо состояния равно сумме динамик по всем рёбрам из этого состояния и плюс один.