Перейти до основного вмісту

Довжина об'єднання відрізків

Задано nn відрізків на прямій, кожен з яких описано парою координат (ai1,ai2)(a_{i1}, a_{i2}). Нам потрібно знайти довжину їхнього об'єднання.

Наведений нижче алгоритм запропонував Klee у 1977 році. Він працює за O(nlogn)O(n\log n) і доведено, що він асимптотично оптимальний.

Коли підходить цей алгоритм?
  • Відрізки лежать на одній прямій (одновимірний випадок), а не на площині?
  • Потрібна саме сумарна довжина об'єднання, без подвійного підрахунку накладань?
  • Усі відрізки відомі заздалегідь, тож можна один раз відсортувати їхні кінці й пройти зліва направо?

Розв'язок

Зберігаємо в масиві xx кінці всіх відрізків, відсортовані за їхніми значеннями. А додатково зберігаємо, чи це лівий кінець, чи правий кінець відрізка. Тепер проходимо по масиву, підтримуючи лічильник cc наразі відкритих відрізків. Щоразу, коли поточний елемент є лівим кінцем, ми збільшуємо цей лічильник, а в іншому випадку зменшуємо його. Щоб обчислити відповідь, ми беремо довжину між двома останніми значеннями xx, тобто xixi1x_i - x_{i-1}, щоразу, коли переходимо до нової координати й при цьому наразі відкрито принаймні один відрізок.

Реалізація

int length_union(const vector<pair<int, int>> &a) {
int n = a.size();
vector<pair<int, bool>> x(n*2);
for (int i = 0; i < n; i++) {
x[i*2] = {a[i].first, false};
x[i*2+1] = {a[i].second, true};
}

sort(x.begin(), x.end());

int result = 0;
int c = 0;
for (int i = 0; i < n * 2; i++) {
if (i > 0 && x[i].first > x[i-1].first && c > 0)
result += x[i].first - x[i-1].first;
if (x[i].second)
c--;
else
c++;
}
return result;
}