Задача
В черный ящик кладутся листки с написанными на них числами. На каждом листке — ровно одно целое число. Иногда некоторые листки исчезают из ящика. После каждого события (когда в ящик положили листок, или когда из ящика исчез листок), нужно вывести число, которое встречается чаще всего на листках, находящихся в данный момент в ящике. Если таких чисел несколько, выведите наименьшее.
Входные данные
Первая строка содержит количество событий [latex]n[/latex] [latex]\left(1 \le n \le 2 \times 10^{5} \right)[/latex]. Каждая из следующих n строк содержит описание одного события:
- [latex]+ x[/latex] — положен листок с числом [latex]x[/latex] [latex]\left(1 \le x \le 10^{6} \right)[/latex];
- [latex]- x[/latex] — исчез листок с числом [latex]x[/latex] (гарантируется, что в ящике был хотя бы один листок с числом [latex]x[/latex]).
Выходные данные
Вывести в точности [latex]n[/latex] строк — по одной для каждого события. Каждая строка должна содержать одно число — ответ к задаче. Если после какого-то события ящик оказался пуст, следует вывести [latex]0[/latex].
Тесты
Входные данные |
Выходные данные |
3
+ 1
— 1
+ 2 |
1
0
2 |
6
+ 1
+ 1000000
— 1
+ 4
+ 4
— 1000000 |
1
1
1000000
4
4
4 |
8
+ 71
+ 91
+ 99
+ 71
— 71
— 91
— 71
— 99 |
71
71
71
71
71
71
99
0 |
Код программы
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
|
#include <iostream> using namespace std; template <class POINTER_TYPE, class TYPE> class segment_tree { TYPE *SegmentTree; POINTER_TYPE base_capacity = 0; TYPE (*g)(TYPE, TYPE); TYPE neutral; TYPE result_on_segment_in ( POINTER_TYPE v, POINTER_TYPE tl, POINTER_TYPE tr, POINTER_TYPE l, POINTER_TYPE r ) { if (l > r) return neutral; else if (l == tl && r == tr) return SegmentTree[v]; else { POINTER_TYPE tm = (tl + tr) / 2; return g(result_on_segment_in(v*2, tl, tm, l, min(r,tm)), result_on_segment_in(v*2+1, tm+1, tr, max(l,tm+1), r)); } } void make_monoid_and_fill_rest(TYPE *NewTree, const POINTER_TYPE &n, TYPE f(TYPE, TYPE), const TYPE &neutr) { g = f; neutral = neutr; for(POINTER_TYPE i = base_capacity+n; i < 2*base_capacity; ++i) { NewTree[i] = neutral; } for(POINTER_TYPE i = base_capacity-1; i > 0; --i) { NewTree[i] = g(NewTree[2*i], NewTree[2*i+1]); } SegmentTree = NewTree; } void fix_capacity(const POINTER_TYPE &base_array_size) { for (base_capacity = 1; base_capacity < base_array_size; base_capacity <<= 1); } public: void read_and_construct(const POINTER_TYPE amount_of_elements, TYPE preprocessing_function(const POINTER_TYPE&), TYPE f(TYPE, TYPE), const TYPE neutr) { fix_capacity(amount_of_elements); TYPE *NewTree = new TYPE[base_capacity*2]; for(POINTER_TYPE i = 0; i < amount_of_elements; ++i) { NewTree[base_capacity+i] = preprocessing_function(i); } make_monoid_and_fill_rest(NewTree, amount_of_elements, f, neutr); } void assign(const POINTER_TYPE index, const TYPE new_value) { SegmentTree[base_capacity+index] = new_value; for (POINTER_TYPE i = (base_capacity+index)/2; i > 0; i /= 2) { SegmentTree[i] = g(SegmentTree[2*i], SegmentTree[2*i+1]); } } TYPE result_on_segment (POINTER_TYPE l, POINTER_TYPE r) { return result_on_segment_in(1, 0, base_capacity-1, l, r); } }; struct leaf { unsigned int number, amount; leaf() { number = 2000000; amount = 0; } leaf(unsigned int a) { number = a, amount = 0; } }; leaf max_leafs(leaf a, leaf b) { return (a.amount == b.amount) ? (a.number < b.number) ? a : b : (a.amount > b.amount) ? a : b; } leaf constructor(const unsigned int &i) { return leaf(i); } int main() { segment_tree <unsigned int, leaf> box; box.read_and_construct(1000001, constructor, max_leafs, leaf()); unsigned int n, index; char operation; leaf tmp; scanf("%u\n", &n); ++n; while (--n) { scanf("%c %u\n", &operation, &index); tmp = box.result_on_segment(index, index); if (operation == '+') ++tmp.amount; else --tmp.amount; box.assign(index, tmp); printf("%u\n", (box.result_on_segment(0, 1000000)).number); } return 0; } |
Решение задачи
Проанализируем задачу: так как требуется вывести число, которое встречается чаще всего на листках, находящихся в ящике после запроса удаления/добавления, а если таких чисел несколько, то вывести наименьшее, то задачу можно переформулировать следующим образом:
«Даётся последовательность (массив) объектов
leaf [latex]x_{1}[/latex], [latex]x_{2}[/latex], [latex]x_{3}[/latex], [latex]\ldots[/latex], [latex]x_{999999}[/latex], [latex]x_{1000000}[/latex], представляющих из себя пару
(number, amount)[latex]=x_{i}=\left(i, a_{i}\right) \in {\mathbb{N}_{0}}^{2}[/latex], где первые элементы пар [latex]i[/latex] представляет из себя число/номер листка, а вторые элементы [latex]a_{i}[/latex] — число листков с этим номером. Изначально все элементы пар [latex]a_{i}[/latex] равны нулю (так как изначально ящик пуст). Для запросов первого типа [latex]+ x[/latex] необходимо увеличивать на единицу число [latex]a_{i}[/latex] объекта, у которого номер [latex]i[/latex] равен [latex]x[/latex], а для запросов второго типа — уменьшать. Для каждого запроса необходимо вывести число [latex]j[/latex], удовлетворяющее условию [latex]j = \min\limits_{i \in \mathbb{K}}{i}[/latex], где [latex]\mathbb{K} = \{i \mid a_{i} = \max\limits_{k \in \{1, 2, \ldots, 1000000\}}{a_{k}} \}[/latex]».
Иными словами, число [latex]i[/latex] соответствует некоторому элементу [latex]x_{i} = \left(i, a_{i}\right)[/latex], который в свою очередь определяется операцией такой, что [latex]i[/latex] и [latex]a_{i}[/latex] удовлетворяют приведённым выше условиям. Очевидно, что данная операция является ассоциативной (как объединение минимума и максимума на заданных множествах), а потому для решения задачи воспользуемся универсальным деревом отрезков.
Создадим дерево отрезков
box методом
read_and_construct из объектов
leaf. Так как нумерация листков начинается с единицы, а их число не превышает [latex]10^{6}[/latex], зададим размер базы дерева отрезков [latex]10^{6}+1[/latex], добавив неё элемент с индексом [latex]0[/latex]. Модифицируем метод
read_and_construct таким образом, чтобы в функцию-препроцессор передавался номер элемента [latex]i[/latex], дабы была возможность задавать элементам [latex]x_{i}[/latex] их первоначальные значения [latex]\left(i, 0\right)[/latex]. Вышеупомянутую операцию назовём
max_leafs и определим таким образом, чтобы она принимала два аргумента [latex]x_{i} = \left(i, a_{i}\right)[/latex] и [latex]x_{j} = \left(j, a_{j}\right)[/latex] и возвращала тот из них, у которого значение [latex]a[/latex] является большим, а в случае равенства этих значений — аргумент с меньшим индексом. Нейтральным элементом относительно данной операции будет, очевидно, пара [latex]\left(+\infty, 0\right)[/latex], но в силу того, что номера элементов не превышают [latex]10^6[/latex], вместо неё мы будем пользоваться парой [latex]\left(2 \times 10^{6}, 0\right)[/latex].
Далее при запросах вида [latex]+ x[/latex] будем увеличивать соответствующее значение [latex]a_{x}[/latex] пары [latex]\left(x, a_{x}\right)[/latex] на единицу, а при запросах вида [latex]- x[/latex] — уменьшать. Для обоих запросов будем выводить номер заданного листка, который удовлетворяет приведённым в задаче условиям, с использованием метода
result_on_segment на всём отрезке [latex]\left[0, 10^{6}\right][/latex]. Ответом для каждого запроса будет значение
number пары, которую вернёт метод.
Примечание: ситуация когда ящик становится пустым нигде не обрабатывается, но в силу того, что мы определили массив на отрезке [latex]\left[0, 10^{6}\right][/latex] вместо [latex]\left[1, 10^{6}\right][/latex] в нём всегда есть пара [latex]\left(0, 0\right)[/latex] (листки с номером [latex]0[/latex], число (значение [latex]b[/latex]) которых всегда равно [latex]0[/latex] в силу того, что листки с номером [latex]0[/latex] в ящик не добавляются). Так как определённая нами операция всегда возвращает минимальный номер листка, число которого максимально, то в случае, когда ящик пуст (т.е. значения всех [latex]a_{i} = 0, i = 0, 1, \ldots, 10^{6}[/latex]) будет выводится номер листка [latex]0[/latex]. Этот побочный эффект данного нами определения массива решает эту ситуацию и завершает решение задачи.
Ссылки
Related Images: