Блог пользователя dimss

Автор dimss, история, 22 месяца назад, По-русски

Мне казалось, что этого нигде нет и я придумал что-то новое, но после того как я предложил задачу на codeforces, один из координаторов сказал, что пару лет назад уже видел задачу, где использовалось что-то похожее. Поэтому задачу отклонили. Так как я не смог придумать более интересную задачу на этот трюк, я просто напишу об этом в блоге.

Задача

Пусть дана какая-то задача, которую мы умеем решать с помощью центроидной декомпозиции. Например такая. Дано дерево, в каждой вершине записано число a[i]. Поступают 2 типа запросов.

  1. Даны $$$v$$$, $$$d$$$, $$$c$$$. Надо перекрасить все вершины на расстоянии не больше $$$d$$$ от $$$v$$$ в цвет $$$c$$$.

  2. Дана $$$v$$$. Надо узнать цвет вершины $$$v$$$.

Теперь эта задача не на дереве, а на связном графе. Но есть одно очень интересное ограничение. m — n + 1 $$$\le$$$ 100. То есть в дерево добавили не больше 100 рёбер.

Сначала напомню как решалась задача на дереве. Мы строили дерево центроидов глубины $$$O(\log n)$$$, для каждого центроида хранили структуру, которая умела красить вершины от $$$C$$$ на расстоянии не более $$$d$$$ и узнавать цвет вершины. Эта структура называется стек, в котором хранятся тройки элементов (расстояние, цвет, время). При чем расстояние строго убывает. Тогда при покраске надо удалить некоторые элементы с конца стека и положить новый элемент. А при запросе цвета ответ можно найти бинпоиском по локальной глубине $$$v$$$.

Мы научились отвечать на запросы, если красим вершины от заданного центроида. Теперь переберем все центроиды $$$C$$$, которые являются предками $$$v$$$ в дереве центроидов и для них сделаем операции выше. Если запрос первого типа, и мы красим от $$$v$$$ на расстояние не более $$$d$$$, то от $$$C$$$ надо покрасить на расстояние не более $$$d - dist(v, C)$$$ Если мы сейчас отвечаем на запрос второго типа, то из всех цветов надо выбрать тот, у которого максимальное время. Почему это работает? Предположим, что был запрос покраски от $$$v$$$, который задел $$$u$$$, цвет которой мы хотим сейчас узнать. Тогда существует центроид $$$C = lca(v, u)$$$ в дереве центроидов. $$$C$$$ — общих центроид и для $$$v$$$, и для $$$u$$$. Причем, он находится на пути от $$$v$$$ до $$$u$$$ в исходном дереве. Тогда когда мы переберем $$$C$$$, правильный ответ нам гарантирован.

Но теперь, когда не дерево, а граф, то построить дерево центроидов нельзя. Давайте переделаем определение центроида и построим нечто похожее. get(G) будет выдавать новый центроид по следующему алгоритму:

  1. Если в G есть цикл, то верни любую вершину на цикле.

  2. Если G — дерево, то верни любой обычный центроид.

Еще одно изменение в постройке — это то, что теперь глубины вершин надо искать не dfs-ом, а bfs-ом. Тогда дерево новых центроидов будет выглядеть как бамбук из 100 вершин, к которому подвешено дерево обычных центроидов. Я утверждаю, что тогда алгоритм выше будет корректно работать. Опять же, пусть была покраска в $$$v$$$, которая задевает $$$u$$$ и мы хотим узнать цвет $$$u$$$. Тогда существует вершина $$$C$$$, которая является предком $$$lca(v, u)$$$ (обратите внимание, что не ровно lca, а предком lca), что крайчайший путь из $$$v$$$ в $$$u$$$ проходит через вершину $$$C$$$. И когда мы ее переберем, посчитается правильный ответ.

Обработка первого запроса амортизировано занимает $$$O(m - n + 1 + \log n)$$$, а второго $$$O((m - n + 1 + \log n) \log n)$$$.

Другие задачи

Все задачи, который я знаю и умею решать, где используются центроиды для ответа на запросы (таких не очень много), я умею обобщать на граф. Но, к сожалению, не любую задачу на дереве можно так обобщить. Например, если центроиды используются для подсчета количества путей в дереве (более точно, количество пар $$$v$$$, $$$u$$$), то здесь некоторые пути будут учитываться несколько раз, если между ними несколько крайчайших путей. Ответ, полученный таким способом дает неплохую верхнюю границу. Реальный ответ меньше не больше чем на $$$(m - n + 1)n$$$.

Спасибо andreyDagger за перевод на английский язык.

  • Проголосовать: нравится
  • +164
  • Проголосовать: не нравится

»
22 месяца назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

Автокомментарий: текст был обновлен пользователем dimss (предыдущая версия, новая версия, сравнить).

»
22 месяца назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

It's so cool!

»
22 месяца назад, # |
  Проголосовать: нравится +11 Проголосовать: не нравится

It's so cool!

»
22 месяца назад, # |
  Проголосовать: нравится +34 Проголосовать: не нравится

Nice idea

»
22 месяца назад, # |
  Проголосовать: нравится +8 Проголосовать: не нравится

Could you please describe the construction process a bit more broadly? BTW great blog!

  • »
    »
    22 месяца назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    1. Trying to find a cycle in the graph with dfs.
    2. If the cycle is found, we take as C any vertex from the cycle
    3. If the loop is not found, then it is a tree. We start dfs2 from any vertex and calculate the sizes of the subtrees. 3.2. Run dfs3 from the same vertex as dfs2, which will find the centroid. If all neighbors have sz[u] <= sz[v] / 2, then v is a centroid. Otherwise, we run dfs3 from the largest neighbor.
    4. Running bfs from C to search for depths.
    5. Remove C from the graph, run recursively from all connected components.
»
22 месяца назад, # |
  Проголосовать: нравится +16 Проголосовать: не нравится

Strongly reminds me of this problem from 300iq contest at Petrozavodsk 2020 Winter with a similar idea of generalizing the centroid decomposition for some special graph

»
22 месяца назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

I'm pretty sure there was a problem like this in codechef a few years ago. Cool trick though.

»
22 месяца назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

For this particular problem, you can just consider any spanning tree, find the vertices that are endpoints to the extra edges, do some bfs from all these special vertices to compute distances to each vertex, and then apply the operation and query for each rooted tree independently as well as for the unrooted spanning tree (you need centroid decomp for this part).

Complexity is $$$O(q((m-n+1) + \log n))$$$. You can even do it with $$$O(n)$$$ memory if you are allowed to process the queries offline.

»
14 месяцев назад, # |
Rev. 3   Проголосовать: нравится 0 Проголосовать: не нравится

He ignored me 😭