Вы находитесь на странице: 1из 3

Динамика по поддеревьям

Здесь я просто разберу одну задачу (с вариациями).

Напомню, что как только мы видим дерево (в любой задаче), мы его “подвешиваем”,
то есть выбираем корень. Почти все задачи про деревья удобно решать на
подвешенном дереве, где у каждой вершины кроме корня есть ровно один родитель и,
возможно, несколько детей.
(Это все удобно хранить как вектор списков детей каждой вершины, и вектор
родителей, если надо)

Задача
Дано обычное дерево. Надо найти одно число - сумму расстояний между каждой
парой вершин.

(В такой формулировке это простая задача, сядьте и попробуйте за 5 минут


придумать, как это сделать)

Решение
Конкретно в такой формулировке задачу можно решить почти без динамики по
поддеревьям, если разбить эту сумму расстояний по каждому ребру.

Вопрос: Сколько раз в этой сумме учитывается конкретное ребро?

Ответ: Столько раз, сколько путей проходит через это ребро. А именно - K * (N-K), где
K вершин находится по одну сторону от этого ребра, а N-K - по другую (это количество
способов выбрать начало и конец пути).

Тогда если мы научимся находить размер каждого поддерева, то задачу мы решили


(для каждого ребра вычислим ответ через это K).
Размер поддерева - это самая тривиальная динамика по поддеревьям, которая может
быть, так что задача простая:
size[x] = SUM[y - ребенок x] size[y] + 1

Усложним задачу
Пусть теперь
а) дерево взвешенное (у ребер есть вес)
Решение не меняется (просто надо умножить на вес ребра).

б) надо найти не сумму ВСЕХ расстояний, а для каждой вершины посчитать сумму
расстояний до неё от каждой другой вершины.
А вот теперь наше решение не прокатывает. Надо придумывать нормальное решение.

Решение
Что хранить как динамику? Очевидно, то, что просят. Давайте хранить в вершине
сумму всех расстояний от этой вершины до ВСЕХ остальных. Но такую динамику даже
непонятно как инициализировать быстро. Тогда давайте для начала насчитаем хотя
бы сумму расстояний до всех вершин в ПОДДЕРЕВЕ.

Такую динамику легко насчитать DFS-ом: в листьях это 0, а переход тоже не очень
сложный: сумма расстояний от вершины до всех потомков - это просто сумма
значений динамики для детей - но надо еще учесть, что в каждом из этих расстояний
путь продлился на одно ребро, вес которого мы знаем (пусть вес ребра, ведущего от
родителя y к y - это w[y]).

dp[x] = SUM[y - ребенок x] (dp[y] + size[y] * w[y])

Заметим, что мы используем в этой формуле size[x], то есть у нас наша динамика
зависит от еще одной. Мы можем не запускать два раза DFS, а прямо походу DFS
насчитывать обе динамики. В ДП по поддеревьям одни динамики все время
используют другие - это нормально, не бойтесь вводить побольше динамик.

Таким образом мы “снизу вверх” насчитали массив dp, где лежит что-то похожее на
ответ - сумма расстояний от вершины до всех потомков.

Заметим, что после этого в корне уже лежит ответ! То есть для корня задачу мы
решили, потому что потомки корня - это как раз все остальные вершины.

Давайте теперь запустим второй DFS и попытаемся теперь найти настоящий ответ (в
массив dp2, например) для каждой вершины, пересчитывая не через детей, а через
родителя (для корня причем ответ мы уже знаем).

Если мы знаем расстояние от родителя до ВСЕХ, как найти расстояние от нас до


ВСЕХ? Давайте найдем расстояние от родителя до ВСЕХ, КРОМЕ НАШЕГО
ПОДДЕРЕВА, “продлим” каждый из этих путей на одно ребро (как мы раньше делали,
просто добавив количество вершин, не лежащих в поддереве), и прибавим ответ в
нашем поддереве.

dp2[x] = (dp2[родитель x] - dp[x] - size[x] * w[x]) + (N - size[x]) * w[x] + dp[x]


= dp2[родитель x] + (N - 2 * size[x]) * w[x] - формула даже немного упростилась, и в ней
больше нет dp[x], ну окей.

Эту динамику мы считаем уже “сверху вниз” (потому что через родителя, а не через
детей).
Вот и все, мы решили задачу за два DFS. Писать кода нужно довольно мало, а вот
придумывать эти переходы - это и есть самое интересное и сложное (возможно я где-
то ошибся, напишите мне про это).

Идеи
1) Считайте сразу несколько динамик, которые зависят друг от друга
2) На примере этой задаче видно, что иногда выгодно считать ДП снизу вверх,
после чего в корне появляется правильный ответ, и потом сверху вниз.
3) Еще бывает такая идея, которая в этой задаче едва встретилась: насчитывать
динамику “для всего поддерева, кроме вот этого ребенка” или даже “для k
первых детей” (в обоих случаях динамика все еще линейная (!), так как
суммарно сыновей N-1).
В этой задаче нам нужно было расстояние от родителя до ВСЕХ, КРОМЕ
НАШЕГО ПОДДЕРЕВА, и мы вместо того, чтобы заводить еще одну dp, мы
просто вычли из суммы это поддерево, нам повезло, что формула простая.
А вот иногда бывает выгодно записывать это в динамику.

Самая главная задача на контесте - F (там в основном и используются эти идеи).


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

Вам также может понравиться