Приближается Международная олимпиада
по информатике и во вьетнамскую команду следует набрать лучших участников со
всей страны. К счастью, в команду смогли набрать n хороших участников, пронумерованных от 1 до n. Для выбора среди них наилучших решили организовать три
соревнования. Каждый из n
конкурсантов принял участие во всех трех соревнованиях, при этом никакие два
участника не имеют одинаковых результатов ни в одном из соревнований. Будем
говорить что участник А лучше участника В, если А стоит по рангу перед В во
всех трех соревнованиях. Участник A является наилучшим, если ни один из других
участников не лучше A. Лидеры вьетнамской команды хотят знать число наилучших
участников.
Вход. Первая
строка содержит количество тестов t
(1 ≤ t ≤ 100 ). Далее
следует описание t тестов. Первая
строка содержит количество участников n
(3 ≤ n ≤ 100000). Каждая
из следующих n строк задает
результаты одного участника и содержит числа ai, bi,
ci (1 ≤ ai, bi, ci
≤ n) – ранги i-го участника в первом, втором и
третьем соревнованиях.
Выход. Для
каждого теста вывести в отдельной строке количество наилучших участников.
Пример входа |
Пример выхода |
1 3 1 2 3 2 3 1 3 1 2 |
3 |
дерево отрезков
Каждый
участник представлен тройкой (a, b, c).
Лучше выступет тот участник, у которого ранг лучше (меньше). Например, участник
A = (3, 1, 6) лучше
участника B = (5, 2,
9) так как его ранги во всех трех
соревнованиях лучше.
Если A = (3, 1, 6) и B = (5, 2, 3), то участники А и B не сравнимы между собой, они оба относительно друг
друга являются наилучшими (никакой другой участник не является лучше А или
лучше B). Отсортируем тройки по
первой компоненте (по условию все значения ai,
bi, ci между собой различны).
Рассмотрим
на плоскости n точек с координатами (bi, ci). Если относительно точки (bi, ci)
не существует такой точки (bj,
cj), что j < i и которая находится одновременно левее и ниже, то она является
превосходной.
Рассмотрим
массив a[1; n], каждый элемент
которого изначально равен бесконечности. Построим по нему дерево отрезков,
поддерживающего операцию минимума. Рассмотрим пары (bi, ci)
в порядке возрастания их соответствующих ai.
Найдем минимум на отрезке [1; bi].
Если он больше ci, то в
квадранте левее и ниже от (bi,
ci) не лежит ни одна из уже
рассмотренных точек. Следовательно текущая тройка превосходная. Далее установим
a[bi] = ci и перейдем к рассмотрению
следующей точки.
Пример
Рассмотрим
следующий тест:
1
5
1 1 4
2 5 2
3 3 5
4 2 1
5 4 3
Тройки для
удобства уже отсортированы по первой компоненте. Разместим на плоскости пары (bi, ci):
Зеленым
цветом отмечены превосходные тройки.
Реализация алгоритма
Объявим
константы и рабочие структуры данных. Дерево отрезков SegmentTree будет
поддерживать минимум.
#define MAX 100010
#define INF 2000000000
struct Strength
{
int a, b, c;
} Fight[MAX];
int SegmentTree[4*MAX];
Участников будем упорядочивать по возрастанию первой
компоненты.
int cmp(Strength x, Strength y)
{
return x.a < y.a;
}
Построение дерева отрезков. Во все его вершины заносим
значение +∞.
void BuildTree(int
Vertex, int LeftPos, int
RightPos)
{
if (LeftPos == RightPos) SegmentTree[Vertex] = INF;
else
{
int Middle = (LeftPos + RightPos) / 2;
BuildTree(2*Vertex,
LeftPos, Middle);
BuildTree(2*Vertex+1, Middle+1, RightPos);
}
SegmentTree[Vertex] =
INF;
}
Вычисление минимума на отрезке.
int GetMin(int
Vertex, int LeftPos, int
RightPos, int Left, int
Right)
{
if (Left > Right) return
INF;
if ((Left == LeftPos) && (Right == RightPos))
return
SegmentTree[Vertex];
int Middle = (LeftPos + RightPos) / 2;
return min(GetMin(2*Vertex, LeftPos, Middle, Left,
min(Right,Middle)),
GetMin(2*Vertex+1, Middle+1, RightPos,
max(Left,Middle+1),Right));
}
Единичное присваивание: m[Position] = NewValue.
void update(int
Vertex, int LeftPos, int
RightPos, int Position,
int NewValue)
{
if (LeftPos == RightPos) SegmentTree[Vertex] =
NewValue;
else
{
int Middle = (LeftPos + RightPos) / 2;
if (Position <= Middle)
update(2*Vertex,
LeftPos, Middle, Position, NewValue);
else update(2*Vertex+1, Middle+1, RightPos, Position,
NewValue);
SegmentTree[Vertex]
=
min(SegmentTree[2*Vertex],SegmentTree[2*Vertex+1]);
}
}
Основная часть программы. Читаем входные данные.
scanf("%d",&tests);
while(tests--)
{
scanf("%d",&n);
for(i = 0; i < n; i++)
scanf("%d %d %d",&Fight[i].a,
&Fight[i].b, &Fight[i].c);
Сортируем участников по первой компоненте. У кого
меньшее место – тот и победитель.
sort(Fight,Fight+n,cmp);
Строим дерево отрезков.
BuildTree(1,1,n);
Последовательно обрабатываем участников. В переменной Result подсчитываем количество наилучших
участников.
for(Result = i = 0; i < n; i++)
{
int c = GetMin(1,1,n,1,Fight[i].b);
if (c > Fight[i].c)
{
Result++;
update(1,1,n,Fight[i].b,Fight[i].c);
}
}
Выводим ответ.
printf("%d\n",Result);
}