Количество различных путей в дереве, у которых значение узлов в этом пути больше или равно K

Постановка задачи:
Вам дано целое число N обозначая количество узлов в этом дереве. Теперь вам нужно посчитать, сколько разных путей есть в дереве, так что минимальное значение узла в этом пути больше или равно k,

Формат ввода:

  1. Первая строка содержит общее количество узлов N в этом дереве и положительное целое значение K,
  2. следующий N-1 строки содержат пару целых чисел u, v (значения не разделяются запятыми), что означает наличие ребра между узлами u а также v, в дереве.

Пример:

Входные данные :

4 2  
1 2  
2 3  
3 4  

Ожидаемый результат:

3

Изменить: я не могу думать о том, как подойти к этой проблеме. Поэтому, пожалуйста, предоставьте мне подсказку, чтобы я мог попробовать ее реализовать. Я буду благодарен даже малейшей помощи.

Обновить:

1 <= N <= 10^5
1 <= u,v <= N  
1 <= K <= 10^6  

Наивный подход к такой проблеме не пройдет ни при каких обстоятельствах. Сложность решения должна быть либо O(n**2), либо O(nlogn).

3 ответа

Эта проблема может быть решена с помощью динамического программирования на деревьях, вы можете прочитать об этом здесь https://www.geeksforgeeks.org/dynamic-programming-trees-set-2/.

Давайте разделим задачу на две части. Первая - найти количество путей, которые являются действительными в поддереве узла. u, Вторая часть - найти ответ для узла. u если мы не рассмотрим его поддерево, а перейдем к его родителю и так далее.

Рассмотрим 1 как корень дерева.

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

//u is the current node - p is the parent
void dfs1(int u, int p) {
    for (int i = 0; i < g[u].size(); i++) { //iterate through the adjacency of node u
        int v = g[u][i]; // v is the child of u
        if (v != p) { //in case v is the parent just ignore it
            dfs1(v, u); //go to node v
            if (u >= k && v >= k) { //if value of u or v is less than k then we cant start at this node so it should be 0
                in[u] += in[v] + 1; //add to number of paths of u the number of paths starting from the node v + 1 (+1 because we can also go from u to v)
            }
        }
    }
}

Для решения второй части нам нужен массив out[] с out[u] быть число путей, которые действительны, если мы не рассматриваем поддерево u который собирается к родителю u и так далее.

давайте позвоним родителю uP[u], Вычислить out[u] мы будем полагаться на p[u], out[i] количество допустимых путей, если мы идем к p[u]и что мы можем сделать, когда достигнем p[u] либо пройти его поддерево (исключая ветвь u конечно) или посетите p[p[u]] (родитель родителя u) так out[u] является (out[p[u]] + 1) + (in[p[u]] - in[u] - 1),

// u is the current node - p is the parent
void dfs2(int u, int p) {
    for (int i = 0; i < g[u].size(); i++) { // iterate through the adjacency of node u
        int v = g[u][i]; // v is the child of u
        if (v != p) { // in case v is the parent just ignore it
            if (v >= k && u >= k) { // if value of u or v is less than k then we cant start at this node so it should be 0
                out[v] += (out[u] + 1); //add to the number of paths of v the number of paths from it's parent (u) + 1 if we dont use the subtree of u
                out[v] += (in[u] - in[v] - 1); // add to the number of paths of v the number of paths from it's parent (u)
                // we should subtract in[v] + 1 because we want to exclude this branch from the subtree of the parent
            }
            dfs2(v, u); // go to node v
        }
    }
}

Чтобы найти ответ, просто сложите все out[u] + in[u] для всех узлов и разделите на 2, потому что каждый путь был вычислен дважды.

сложность O(V+E)

Давайте решим эту проблему в более простом случае, предположим, что все узлы в дереве больше, чем k, поэтому номер действительного пути nC2,

И мы также делаем замечание, что действительный путь не может содержать ни одного узла, который меньше kИтак, нам нужно будет удалить все узлы, которые меньше, чем k из дерева, которое создаст n - k поддерево, таким образом, конечный результат будет

result = sum of nC2 of all subtree

Простой алгоритм:

remove all edges that connect to nodes that less than k

for each node that >= k and not marked as visited
  do bfs to count number of node in that subtree
  result += nC2

return result

Для деревьев, предполагая, что пути, которые мы перечисляем, направлены сверху вниз, мы можем сформулировать это рекурсивно. Позволять f(T, k) представлять кортеж, [a, b], где a количество различных допустимых путей в T которые начинаются в T; а также bколичество различных допустимых путей в T которые начинаются на нижнем узле. Все узлы в допустимых путях имеют значение, большее или равное k,

Тогда (код Python):

def f(T, k):
  if not T["children"]:
    return [0, 0]

  result = [0, 0]

  for c in T["children"]:
    [a, b] = f(c, k)
    result[1] += a + b

    if T["value"] >= k <= c["value"]:
      # One valid path from T to c
      # plus extending all the paths
      # that start at c
      result[0] += 1 + a

  return result

Ответ будет тогда a + bпосле звонка f от корня дерева.

Выход:

T = {
  "value": 1,
  "children": [
    {
      "value": 2,
      "children": [
        {
          "value": 3,
          "children": [
            {
              "value": 4,
              "children": []
            }
          ]
        }
      ]
    }
  ]
}

print f(T, 2)  # [0, 3]
Другие вопросы по тегам