МууТуб (Срібло)#10422

МууТуб (Срібло) (Розбір)

Для кожного запиту, заданого парою (k_i, v_i) запустимо пошук в глибину або в ширину, починаючи з вершини v_i. При цьому рухатися по графу будемо тільки по ребрам, релевантність яких не менше значення k_i. Порахуємо кількість досягнутих вершин , не рахуючи початкову вершину v_i. Значення і буде відповіддю на запит (k_i, v_i).

Приклад

Фермер Джон встановив наступні зв'язки між відео:

На підставі цих даних можна обчислити релевантність між іншими парами відео:

Тепер розглянемо, які відео будуть рекомендовані при наступних запитах:

Реалізація алгоритму — пошук у ширину

Вхідний граф зберігаємо у списку суміжності .

vector<vector<pair<int, int>>> g;

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

int bfs(int start, int k)
{

Масив найкоротших відстаней у цій задачі не потрібний — досить лише відстежувати, чи була вершина відвідана. Якщо вершина була відвідана, встановлюємо used[i] = 1.

  vector<int> used(n + 1);
  used[start] = 1;

Ініціалізуємо чергу і додаємо в неї стартову вершину .

  queue<int> q;
  q.push(start);

Кількість відвіданих вершин при пошуку у ширину (не рахуючи початкову вершину ) підраховуємо в змінній .

  int res = 0;

Запускаємо пошук у ширину.

  while (!q.empty())
  {

Виймаємо вершину з черги .

    int v = q.front(); q.pop();

Перебираємо ребра, суміжні з вершиною .

    for (auto edge : g[v])
    {

Розглядаємо ребро (v, to) вагою .

     int to = edge.first;
     int kto = edge.second;

Якщо вага ребра менша , то таке ребро не розглядаємо (перехід по ньому заборонений).

      if (kto < k) continue;

Якщо вершина ще не була відвідана, то додаємо її у чергу, позначаємо як відвідану і збільшуємо лічильник відвіданих вершин на .

      if (used[to] == 0)
      {
        q.push(to);
        used[to] = 1;
        res++;
      }
    }
  }

Повертаємо відповідь на запит.

  return res;
}

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

scanf("%d %d", &n, &qu);
g.resize(n + 1);
for (i = 0; i < n - 1; i++)
{
  scanf("%d %d %d", &p, &q, &r);

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

  g[p].push_back({ q, r });
  g[q].push_back({ p, r });
}

Послідовно обробляємо запитів.

while (qu--)
{
  scanf("%d %d", &k, &v);

Запускаємо пошук у ширину з вершини . Виводимо кількість вершин, відвіданих в результаті пошуку у ширину.

  printf("%d\n", bfs(v, k));
}

Реалізація алгоритму – пошук у глибину

Вхідний граф зберігаємо у списку суміжності .

vector<vector<pair<int, int>>> g;

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

int dfs(int v, int k)
{

Позначаємо вершину як відвідану.

  used[v] = 1;

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

  int res = 1;

Перебираємо ребра, суміжні з вершиною .

  for (auto edge : g[v])
  {

Розглядаємо ребро (v, to) вагою .

    int to = edge.first;
    int kto = edge.second;

Якщо вага ребра менша , то таке ребро не розглядаємо (перехід по ньому заборонений).

    if (kto < k) continue;

Якщо вершина ще не була відвідана, то запускаємо з неї пошук у глибину. Кількість відвіданих вершин у піддереві з коренем додаємо до .

    if (used[to] == 0)
      res += dfs(to, k);
  }
  return res;
}

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

scanf("%d %d", &n, &qu);
g.resize(n + 1);
for (i = 0; i < n - 1; i++)
{
  scanf("%d %d %d", &p, &q, &r);

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

  g[p].push_back({ q, r });
  g[q].push_back({ p, r });
}

Послідовно обробляємо запитів.

while (qu--)
{
  scanf("%d %d", &k, &v);

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

  used.assign(n + 1, 0);
  printf("%d\n", dfs(v, k) - 1);
}