Сума відстаней#10761

Сума відстаней (Розбір)

Використаємо систему неперетинних множин. Спочатку кожну вершину занесемо в окрему множину. Переберемо ребра в порядку спадання їхніх ваг, для кожного ребра запустимо функцію Union(u, v). Вона об'єднає множини, в яких знаходяться вершини та .

Розглянемо поточне ребро дерева з вагою . Нехай воно об'єднує множини з розмірами size[u] та size[v]. Усі ребра в цих двох множинах мають вагу не меншу, ніж . Ребро входить у size[u] \cdot size[v] різних шляхів. Оскільки вага мінімальна, то відстані цих шляхів дорівнюють . Тобто внесок цих шляхів у суму довжин усіх відстаней становить w \cdot size[u] \cdot size[v].

Приклад

Розглянемо перший тест.

Граф містить три шляхи:

Сума відстаней між усіма парами вершин дорівнює 1 + 3 + 1 = 5.

Розглянемо другий тест.

Кожну вершину віднесемо до окремої множини.

Перебираємо ребра в порядку спадання ваги. Першим буде ребро . Виконуємо Union(1, 3). Відстань має size[1] \cdot size[3] = 1 \cdot 1 = 1 шлях (з в ).

Наступним буде ребро . Виконуємо Union(4, 5). Відстань має size[4] \cdot size[5] = 1 \cdot 1 = 1 шлях (з в ).

Наступним буде ребро . Виконуємо Union(2, 4). Відстань має size[2] \cdot size[4] = 1 \cdot 2 = 2 шляхи (з в і з в ).

Наступним буде ребро . Виконуємо Union(1, 4). Відстань має size[1] \cdot size[4] = 2 \cdot 3 = 6 шляхів (2 → 4 → 1, 4 → 1, 5 → 4 → 1, 2 → 4 → 1 → 3, 4 → 1 → 3, 5 → 4 → 1 → 3).

Шукана сума відстаней дорівнює 7 + 5 + 6 + 12 = 30.

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

Оголосимо масиви, використовувані системою неперетинних множин.

struct Edge
{
  int u, v, cost;
} temp;
vector<Edge> e;
vector<int> parent, ssize;

Функція — компаратор cmpGreater сортує ребра за спаданням ваги.

int cmpGreater(Edge a, Edge b)
{
  return a.cost > b.cost;
}

Функція Repr повертає представника множини, в якій знаходиться вершина .

int Repr(int v)
{
  if (v == parent[v]) return v;
  return parent[v] = Repr(parent[v]);
}

Функція Union об'єднує множини з елементами та . Реалізуємо евристику з розмірами множин.

void Union(int x, int y)
{
  x = Repr(x); y = Repr(y);
  if (x == y) return;
  if (ssize[x] < ssize[y]) swap(x, y);
  parent[y] = x;
  ssize[x] += ssize[y];
}

Основна частина програми. Читаємо кількість вершин в графі.

scanf("%d", &n);

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

parent.resize(n + 1);
ssize.resize(n + 1);
for (i = 0; i <= n; i++)
{
  parent[i] = i;
  ssize[i] = 1;
}

Читаємо ребра дерева. Зберігаємо їх в масиві .

for (i = 0; i < n - 1; i++)
{
  scanf("%d %d %d", &temp.u, &temp.v, &temp.cost);
  e.push_back(temp);
}

Сортуємо ребра за спаданням ваг.

sort(e.begin(), e.end(), cmpGreater);

В змінній підраховуємо суму відстаней між усіма парами вершин.

res = 0;

Перебираємо ребра. Для кожного ребра вагою запускаємо функцію Union(u, v). В додаємо вклад усіх шляхів з відстанню .

for (i = 0; i < e.size(); i++)
{
  res += 1LL * e[i].cost * ssize[Repr(e[i].u)] * ssize[Repr(e[i].v)];
  Union(e[i].u, e[i].v);
}

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

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

Python реалізація

Оголосимо клас , що описує ребро графа.

class Edge:
  def __init__(self, u, v, cost):
    self.u = u
    self.v = v
    self.cost = cost

Функція — компаратор cmpGreater сортує ребра за спаданням ваги.

def cmpGreater(a, b):
    return a.cost > b.cost

Функція Repr повертає представника множини, в якій знаходиться вершина .

def Repr(v):
  if v == parent[v]: return v
  parent[v] = Repr(parent[v])
  return parent[v]

Функція Union об'єднує множини з елементами та . Реалізуємо евристику з розмірами множин.

def Union(x, y):
  x = Repr(x)
  y = Repr(y)
  if x == y: return
  if ssize[x] < ssize[y]:
    x, y = y, x
  parent[y] = x
  ssize[x] += ssize[y]

Основна частина програми. Читаємо кількість вершин в графі.

n = int(input().strip())

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

parent = list(range(n + 1))
ssize = [1] * (n + 1)
e = []

Читаємо ребра дерева. Зберігаємо їх у списку .

for _ in range(n - 1):
  u, v, cost = map(int, input().split())
  e.append(Edge(u, v, cost))

Сортуємо ребра за спаданням ваг.

e.sort(key=lambda x: x.cost, reverse=True)

В змінній підраховуємо суму відстаней між усіма парами вершин.

res = 0

Перебираємо ребра. Для кожного ребра вагою запускаємо функцію Union(u, v). В додаємо вклад усіх шляхів з відстанню .

for edge in e:
  res += edge.cost * ssize[Repr(edge.u)] * ssize[Repr(edge.v)]
  Union(edge.u, edge.v)

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

print(res)