Малюк може обрати двох пасажирів на відрізку [l; r] тоді і тільки тоді, коли сума двох найменших мас тварин на цьому відрізку не перевищує . Дійсно, якщо якась пара тварин підходить, то пара з двох найлегших тварин підходить тим більше.
Тому можна побудувати дерево відрізків, у якому кожна вершина зберігає два мінімуми на своєму відрізку. Однак нас цікавлять не самі мінімуми, а лише те, чи існують два числа, сума яких не перевищує . Це дозволяє зберігати в кожній вершині лише одне число.
Значення у вершинах. Листки дерева відрізків зберігають маси тварин. Кожна внутрішня вершина зберігає:
, якщо її відрізок містить двох тварин із сумарною масою, що не перевищує ;
мінімальну масу тварини на своєму відрізку в іншому випадку.
Маси тварин додатні, тому значення не збігається з жодною масою і однозначно означає, що знайдено відповідну пару.
Об'єднання дітей. Значення вершини обчислюється зі значень та її дітей. Відповідна пара тварин на відрізку вершини або повністю лежить в одній з її половин, або складається з тварини з лівої половини та тварини з правої половини. Тому:
якщо x = 0 або y = 0, відповідна пара лежить в одній із половин, і значення вершини дорівнює ;
інакше, та — це мінімальні маси в половинах, і найкраща пара тварин з різних половин має сумарну масу x + y. Якщо x + y \le k, значення вершини дорівнює , інакше воно дорівнює \min(x, y).
Із такою функцією об'єднання задача розв'язується стандартним деревом відрізків із точковими оновленнями.
Для запиту типу відповідь "Yes", якщо значення на відрізку [l; r] дорівнює , і "No" в іншому випадку.
Приклад
Розглянемо запити з прикладу, k = 9. Спочатку маси тварин дорівнюють 1, 3, 1, 6, 6, 7:
: дві найлегші тварини мають маси та , 1 + 1 = 2 \le 9, відповідь "Yes";
: 1 + 3 = 4 \le 9, відповідь "Yes";
: маса четвертої тварини стає ;
: 7 + 6 = 13 > 9, відповідь "No";
: 6 + 7 = 13 > 9, відповідь "No";
та : маси першої та третьої тварин стають та .
Перед останнім запитом маси тварин дорівнюють 7, 3, 8, 7, 6, 7. Дерево відрізків для них показано нижче. Для кожної вершини показано її відрізок та збережене значення. Листки виділено жовтим кольором, а вершину зі значенням — помаранчевим.
Обчислимо значення у внутрішніх вершинах:
: 7 + 3 = 10 > 9, значення дорівнює \min(7, 3) = 3;
: 3 + 8 = 11 > 9, значення дорівнює \min(3, 8) = 3;
: 7 + 6 = 13 > 9, значення дорівнює \min(7, 6) = 6;
: 6 + 7 = 13 > 9, значення дорівнює \min(6, 7) = 6;
: 3 + 6 = 9 \le 9, значення дорівнює .
Лише вершина містить : дві найлегші тварини з масами та знаходяться в різних половинах відрізка, і їхня сумарна маса дорівнює . Тому відповідь на останній запит — "Yes".
Реалізація алгоритму
Оголосимо константи та робочі масиви. Масив SegTree зберігає дерево відрізків, а маси тварин зберігаються в масиві . Константа більша за будь-яку масу тварини і використовується як нейтральний елемент для порожнього відрізка. Сума та будь-якої маси не перевищує 2 \cdot 10^9 + 10 і вміщується в тип .
#define MAX 1000001
#define INF 1000000010
int SegTree[4 * MAX];
int a[MAX];Функція f об'єднує значення та дітей. Якщо хоча б одна з дітей містить , її відрізок уже містить двох відповідних пасажирів. Якщо x + y \le k, найлегші тварини лівої та правої половин утворюють відповідну пару. В обох випадках повертається ; інакше повертається мінімум з та .
int f(int x, int y)
{
if (x == 0 || y == 0 || x + y <= k) return 0;
return min(x, y);
}Функція BuildTree будує дерево відрізків з масиву . Вершина відповідає відрізку [lpos; rpos]. Листки зберігають маси тварин, а значення у внутрішніх вершинах обчислюються функцією .
void BuildTree(int v, int lpos, int rpos)
{
if (lpos == rpos)
SegTree[v] = a[lpos];
else
{
int mid = (lpos + rpos) / 2;
BuildTree(2 * v, lpos, mid);
BuildTree(2 * v + 1, mid + 1, rpos);
SegTree[v] = f(SegTree[2 * v], SegTree[2 * v + 1]);
}
}Функція Query повертає значення на відрізку [left; right]: , якщо він містить двох пасажирів із сумарною масою не більше , і мінімальну масу тварини в іншому випадку. Вершина відповідає відрізку [lpos; rpos]. Для порожнього відрізка повертається нейтральне значення .
int Query(int v, int lpos, int rpos, int left, int right)
{
if (left > right) return INF;
if ((lpos == left) && (rpos == right)) return SegTree[v];
int mid = (lpos + rpos) / 2;
return f(Query(2 * v, lpos, mid, left, min(right, mid)),
Query(2 * v + 1, mid + 1, rpos, max(left, mid + 1), right));
}Функція Update присвоює значення елементу з індексом та перераховує значення в усіх вершинах на шляху від листка до кореня. Вершина відповідає відрізку [lpos; rpos].
void Update(int v, int lpos, int rpos, int pos, int val)
{
if (lpos == rpos)
SegTree[v] = val;
else
{
int mid = (lpos + rpos) / 2;
if (pos <= mid) Update(2 * v, lpos, mid, pos, val);
else Update(2 * v + 1, mid + 1, rpos, pos, val);
SegTree[v] = f(SegTree[2 * v], SegTree[2 * v + 1]);
}
}Основна частина програми. Зчитаємо кількість тварин та залишкову вантажопідйомність . Зчитаємо маси тварин у масив , починаючи з індексу .
scanf("%d %d", &n, &k);
for (i = 1; i <= n; i++)
scanf("%d", &a[i]);Побудуємо дерево відрізків з елементів масиву a[1 \ldots n].
BuildTree(1, 1, n);Обробимо запитів по черзі. Тварини нумеруються, починаючи з , і елементи масиву також зберігаються, починаючи з індексу , тому індекси із запитів використовуються без змін.
scanf("%d", &q);
for (j = 0; j < q; j++)
{
scanf("%d %d %d", &t, &l, &r);Запит типу : обчислимо значення на відрізку [l; r]. Якщо воно дорівнює , двох пасажирів знайдено.
if (t == 1)
{
int Res = Query(1, 1, n, l, r);
if (Res == 0) printf("Yes\n"); else printf("No\n");
}Запит типу : присвоїмо масу тварині з індексом .
else
Update(1, 1, n, l, r);
}