Заражене дерево#11724

Заражене дерево (Розбір)

Для кожної вершини обчислимо кількість вершин у піддереві, де вона є коренем. Якщо to_1, to_2, ..., to_k — сини вершини , то

sum[v] = 1 + sum[to_1] + sum[to_2] + ... + sum[to_k]

Якщо вершина є листком дерева, то sum[v] = 1.

Нехай дорівнює кількості врятованих вершин у піддереві з коренем , якщо на даний момент вершина (і тільки вона в піддереві) заражена.

Якщо вершина має тільки одного сина , то dp[v] = sum[to] - 1 (видалена вершина не є врятованою).

Нехай вершина має двох синів to_1 і to_2 (кожна вершина має ступінь не більше ). Тоді у нас є два варіанти:

З двох варіантів слід вибрати той, при якому кількість врятованих вершин буде більшою.

Розглянемо процес пошуку в глибину, коли розглядається ребро (v, to).

Нехай — другий син вершини . Нам слід обчислити значення dp[to] + sum[a] - 1. Спробуємо його знайти, використовуючи тільки вершини і . Нам відомо, що

sum[v] = 1 + sum[to] + sum[a]

Звідки

sum[a] = sum[v] - sum[to] - 1

Отже

dp[to] + sum[a] - 1 = dp[to] + sum[v] - sum[to] - 2

Перебираємо синів вершини і обчислюємо

dp[v] = max(dp[to] + sum[v] - sum[to] - 2)

Приклад

У першому прикладі Хусейн зможе врятувати дві вершини і , обравши своїм першим ходом вершину номер .

Розглянемо другий приклад. Біля кожної вершини поставимо мітки sum[v] / dp[v]. Вершини, обрані Хусейном, відобразимо зеленим кольором.

Наприклад,

dp[1] = max(sum[2] - 1 + dp[5], sum[5] - 1 + dp[2]) = max(2, 2) = 2, \\ sum[1] = 1 + sum[2] + sum[5] = 1 + 3 + 3 = 7

Реалізація алгоритму

Оголосимо робочі масиви.

#define MAX 300005
int sum[MAX], dp[MAX];
vector<vector<int>> g;

Функція dfs здійснює пошук в глибину з вершини . Предком вершини є .

void dfs(int v, int p = -1)
{

Ініціалізуємо значення і .

  sum[v] = 1;
  dp[v] = 0;

Запускаємо пошук в глибину з синів вершини .

  for (int to : g[v])
    if (to != p)
    {
      dfs(to, v);
      sum[v] += sum[to];
    }

Вичисляємо значення .

  for (int to : g[v])
    if (to != p)
    {
      dp[v] = max(dp[v], sum[to] - 1); // if 1 son
      dp[v] = max(dp[v], dp[to] + sum[v] - sum[to] - 2);
    }
}

Основна частина програми. Читаємо вхідні дані.

scanf("%d", &n);
g.clear(); g.resize(n + 1);
memset(dp, 0, sizeof(dp));
memset(sum, 0, sizeof(sum));
for (i = 1; i < n; i++)
{
  scanf("%d %d", &u, &v);
  g[u].push_back(v);
  g[v].push_back(u);
}

Запускаємо пошук в глибину з вершини .

dfs(1);

Виводимо відповідь.

printf("%d\n", dp[1]);