Відстань у дереві#4104

Відстань у дереві (Розбір)

Нехай dp[v][x] — кількість вершин, що знаходяться на відстані від вершини в піддереві з коренем у . Покладемо dp[v][0] = 1, оскільки на відстані від вершини знаходиться тільки сама вершина .

Нехай вершина має синів u_1, u_2, ..., u_p. Тоді кількість вершин, що знаходяться на відстані від вершини в піддереві з коренем у , дорівнює сумі кількості вершин, що знаходяться на відстані x - 1 від кожної з вершин u_1, u_2, ..., u_p.

dp[v][x] = \sum_{i = 1}^p dp[u_i][x-1]

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

Розглянемо шлях довжини , проходящий через корінь піддерева . Нехай відстань від вершини до одного з кінців шляху дорівнює x~(1 \le x \le k - 1). Припустимо, що цей кінець знаходиться у піддереві певного сина u_1. Тоді відстань від вершини до другого кінця шляху дорівнює k - x, причому другий кінець може знаходитися в будь-якому іншому піддереві, відмінному від піддерева з коренем у u_1.

Якщо відстань від першого кінця шляху до вершини дорівнює , то відстань від цього кінця до вершини u_1 дорівнює x - 1. Число способів вибрати такий перший кінець дорівнює dp[u_1][x - 1].

Число способів вибрати другий кінець шляху дорівнює кількості вершин, що знаходяться на відстані k - x від вершини , але не належать піддереву з коренем у u_1. Це кількість дорівнює

dp[v][k - x] - dp[u_1][k - x - 1]

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

dp[u_1][x - 1] \cdot (dp[v][k - x] - dp[u_1][k - x - 1])

Таким чином, загальна кількість таких шляхів дорівнює

dp[v][k] + 1/2 \sum_{i = 1}^p \sum_{x = 1}^{k - 1} dp[u_i][x-1] \cdot (dp[v][k-x]-dp[u_i][k-x-1])

Залишається просумувати зазначене вираз по всім вершинам дерева.

Приклад

Розглянемо дерево, наведене в прикладі.

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

В масиві зберігається представлення дерева у вигляді списків суміжності. Масив використовується для реалізації динамічного програмування.

vector<vector<int> > g;
vector<vector<int> > dp;

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

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

Ініціалізуємо масив .

  dp[v].resize(k+1);
  dp[v][0] = 1;

Перебираємо всі вершини , в які можна перейти з вершини .

  for (int to : g[v])
  {
    if (to == p) continue;

    dfs(to,v);

    for(int x = 1; x <= k; x++)
      dp[v][x] += dp[to][x-1];
  }
}

Функція dfsRes виконує пошук в глибину, починаючи з вершини , і призначена для обчислення шуканої відповіді.

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

  for (int to : g[v])
  {
    if (to == p) continue;

    dfsRes(to,v);

    for(int x = 1; x < k; x++)
      resA += 1LL * dp[to][x-1] * (dp[v][k-x] - dp[to][k-x-1]);
  }

  resB += dp[v][k];
}

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

scanf("%d %d",&n,&k);
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);
}

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

dp.resize(n+1);
dfs(1);

Підраховуємо та виводимо шукану відповідь.

resA = resB = 0;
dfsRes(1);
res = resA / 2 + resB;
printf("%lld\n",res);

Python реалізація

Функція dfs виконує пошук в глибину, починаючи з вершини . Значення vdp. \begin{lstlisting}[language=Python] def dfs(v, p): \end{lstlisting} Ініціалізуємо масив dp[v]. \begin{lstlisting}[language=Python] dp[v] = [0] * (k + 1) dp[v][0] = 1 \end{lstlisting} Перебираємо всі вершини tov. \begin{lstlisting}[language=Python] for to in g[v]: if to == p: continue dfs(to, v) for x in range(1, k + 1): dp[v][x] += dp[to][x - 1] \end{lstlisting} Функція \textbf{dfsRes} виконує пошук в глибину, починаючи з вершини v, і призначена для обчислення шуканої відповіді. \begin{lstlisting}[language=Python] def dfsRes(v, p): global resA, resB for to in g[v]: if to == p: continue dfsRes(to, v) for x in range(1, k): resA += dp[to][x - 1] * (dp[v][k - x] - dp[to][k - x - 1]) resB += dp[v][k] \end{lstlisting} Основна частина програми. Читаємо кількість вершин дерева nk. \begin{lstlisting}[language=Python] n, k = map(int, input().split()) \end{lstlisting} У списку gdp використовується для реалізації динамічного програмування. \begin{lstlisting}[language=Python] g = [[] for _ in range(n + 1)] dp = [[] for _ in range(n + 1)] \end{lstlisting} Читаємо список ребер. Будуємо дерево. \begin{lstlisting}[language=Python] for _ in range(n - 1): a, b = map(int, input().split()) g[a].append(b) g[b].append(a) \end{lstlisting} Першим пошуком в глибину будуємо масив dp$.

dfs(1, -1)

Підраховуємо та виводимо шукану відповідь.

resA, resB = 0, 0
dfsRes(1, -1)
res = resA // 2 + resB
print(res)