Алгоритм Хьюи

Материал из Викиконспекты
Перейти к: навигация, поиск
Задача:
Дано ориентированный дерево, вершины которого раскрашены в цвета. Найти [math]dc:V\rightarrow \{1\ldots k\}[/math], где [math]dc(u) -[/math] число различных цветов в поддереве с корнем в вершине [math]u[/math]. Время работы: [math]O(V)[/math]


Алгоритм решения

Будем в каждой вершине дерева хранить по числу, так, чтобы для каждого поддерева ответом была сумма всех значений в вершинах в данном поддереве. Для начала каждой вершине в качестве значения присвоим [math]1[/math]. Теперь, если бы все вершины имели различные цвета, надо было бы пройти снизу вверх по дереву и просуммировать для каждой вершины числа, записанные в её детях. Но некоторые вершины будут иметь одинаковые цвета, и это надо как-то учитывать. Для этого запустим обход в глубину. Также будем хранить для каждого цвета последнюю посещенную вершину данного цвета в массиве [math]last[k][/math]. Теперь, заходя в [math]i[/math]-ую вершину с цветом [math]col[/math], смотрим: если вершина с таким цветом еще не встречалась, то просто присваиваем [math]last[col]=i[/math], иначе, если вершина с данным цветом уже встречалась, то находим наименьшего общего предка данной вершины и последней вершины с таким цветом и вычитаем из их предка [math]1[/math], присваиваем [math]last[col]=i[/math]. Теперь при выходе из вершины можно просуммировать числа в ее детях и получить ответ для данной вершины, так как для нее все дети уже подсчитаны. Таким образом, алгоритм запускает один обход в глубину, на каждой итерации которого ищет наименьшего общего предка. Если искать наименьшего общего предка за [math]O(1)[/math], к примеру алгоритмом Фарака-Колтона и Бендера, то сложность работы алгоритма будет [math]O(V)[/math].

Пример

№ шага Изображение Описание
0 Algo 0.png Расставим у каждой вершины [math]1[/math].
1 Algo 1.png Выходим из [math]8[/math]-ой вершины. Так как желтых вершин еще не было, запоминаем её как последнюю желтую.
2 Algo 2.png [math]4[/math]-ая вершина. Последняя желтая [math]-\ 8[/math]-ая. Их LCA [math]\ -4[/math]-ая вершина. Вычитаем из значения [math]4[/math]-ой вершины [math]1[/math] и запоминаем текущую как последнюю желтую.
8 8.png Пропустим несколько тривиальных шагов. Выходим из [math]11[/math]-ой вершины. Последней посещенной зеленой была [math]5[/math]-ая (не [math]3[/math]-я). Вычитаем из их LCA ([math]1[/math]-ой вершины) [math]1[/math] и запоминаем [math]11[/math]-ую как последнюю зеленую.
9 9.png Выходим из [math]7[/math]-ой вершины. Последней синей была [math]2[/math]-ая. Вычтем из их LCA [math]1[/math] и запомним [math]7[/math]-ую как последнюю синюю.
суммирование Algo 12.png Пропустим еще два шага. В результате суммирования получаем в каждой вершине ответ на задачу для поддерева.

Псевдокод

int col[MAX_COL], used[MAX_N], sum[MAX_N]

func dfs(Node v):
   used[v] = true
   for [math]u \in v.children[/math]
       if !used[u]
           dfs(u)
       sum[v] += sum[u]
   if last[col[v]] != -1
        sum[lca(v, last[col[v]])]--
   last[col[v]] = v
       
func hugh(int n, int k, Node root):
   for [math]v \in V[/math]
       used[v] = false
       sum[v] = 1
   for i = 1 to k
       last[i] = -1
   dfs(root)

Обоснование корректности

Лемма:
Наименьшим общим предком вершины и группы вершин, предшествующих по времени выхода, является наименьший общий предок данной вершины и последней, предшествующей ей из группы.
Доказательство:
[math]\triangleright[/math]

Рассмотрим дерево как последовательность букв, когда при входе в вершину или выходе из нее записывается ее буква. Пусть рассматриваемая вершина [math]-\ u[/math], а последняя рассмотренная из той же группы [math] -\ v[/math], их наименьший общий предок [math]-\ w[/math]. Рассмотрим два варианта расположения этих двух вершин.

Proof 1.png Proof 2.png

Теперь возьмем вершину [math]z[/math], которая встречается до выхода из [math]v[/math]. Перебрав несложные пять случаев, можно легко убедиться, что наименьший общий предок [math]u[/math] и [math]v[/math] будет ниже, чем наименьший общий предок [math]u[/math] и [math]x[/math].
[math]\triangleleft[/math]

Для того, чтобы учитывать вершины с одинаковым цветом, для каждой вершины требуется найти наименьшего общего предка этой вершины и вершин, предшествующих данной по времени выхода с таким же цветом и вычесть из значения этого предка [math]1[/math]. Так, при конечном подсчете значение наименьшего общего предка данной вершины и любой вершины, предшествующей данной с тем же цветом, уменьшится на [math]1[/math], так как наименьший предок этой точки и любой предшествующей того же цвета находится на пути из наименьшего общего предка этой группы точек. А как раз это и требуется - для каждой пары точек одного цвета учесть данный факт в их наименьшем общем предке. И по лемме, чтобы взять наименьшего общего предка текущей вершины и всех предшествующих вершин с данным цветом, надо взять наименьшего общего предка данной вершины и предыдущей вершины с данным цветом, он будет наименьшим для всех.

Hugh.png

См. также

Алгоритм Тарьяна поиска LCA за O(1) в оффлайн

Метод двоичного подъема