Зараження#10367

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

Щоб заразити всі ферми, необхідно виконати два типи дій:

Зауважимо, що подвоєння і переходи можуть відбуватися в різних місцях дерева після зараження.

Нехай на ферму прийшла одна хвора корова. Якщо ферма має дітей у дереві, то вона повинна відправити заражених корів до сусідів, тому їй потрібно мати щонайменше корів.

Спочатку на фермі є корова. Здійснюємо подвоєння: 1 \to 2 \to 4 \to 8.... Мінімальна кількість подвоєнь, щоб отримати не менше корів, дорівнює ⌈log_2 x⌉.

Таким чином, мінімальна кількість днів, необхідна для розповсюдження хвороби по всіх фермах, дорівнює

(n - 1) + ∑_{v = 1}^n ⌈log_2~кількість~дітей~верхівки~v⌉

За умовою задачі достатньо, щоб у якусь мить на кожній фермі побувала бодай одна заражена корова. Умова не говорить, що після зараження всіх ферм на кожній фермі має залишитися хоча б одна заражена корова.

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

Дерево зберігаємо у вигляді списку суміжності .

vector<vector<int>> g;

Функція dfs виконує обхід у глибину, починаючи з вершини , і повертає кількість днів, необхідних для подвоєння заражених корів у всіх вершинах піддерева з коренем у . Попередником вершини є .

int dfs(int v, int par = 0)
{
  int ans = 0;
  for (int to : g[v])
    if (to != par)
      ans += dfs(to, v);
  return ans + ceill(log2(g[v].size()));
}

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

scanf("%d", &n);
g.resize(n + 1);
for (i = 0; i < n - 1; i++)
{
  scanf("%d %d", &a, &b);
  g[a].push_back(b);
  g[b].push_back(a);
}

Запускаємо пошук у глибину з вершини і обчислюємо мінімальну кількість днів, необхідну для зараження всіх ферм. Відповідь зберігаємо у змінній .

res = dfs(1) + n - 1;

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

printf("%d\n", res);