http://judge.openfmi.net/mediawiki/index.php/S_:_timus_:_1019_-_A_Line_painting


Условие

Условието на английски в acm.timus.ru

Времево ограничение: 2.0 секунди

Ограничение по памет: 16 MB


Сегмент на числовата ос от 0 до 109 е оцветен в бяло. След това части от него са оцветени в черно, след това части в бяло и т.н. Общо са извършени N преоцветявания (1 ≤ N ≤ 5000).

Трябва да се напише програма, която намира най-големия непрекъснат бял интервал след всички оцветявания.

Вход

Първият ред от входа съдържа само числото N. Следващите N реда съдържат информация за оцветяванията. Всеки ред изглежда по следния начин:

ai bi ci

където ai and bi са цели числа, ci е символа 'b' или 'w'; ai, bi, ci са разделени с интервал. Трите части се използват, за да се означи оцветяване на сегмент от ai до bi с цвят ci ('w' — бяло, 'b' — черно). Приемете, че 0 < ai < bi < 109.

Изход

Изходът се състои от две числа x и y (x < y), разделени с интервал. Тези числа трябва да определят най-дългия непрекъснат бял сегмент. Ако има повече от един такъв сегмент, изходът трябва да съдържа този с най-малък x.


Решение

Поради дадените ограничения в условието е невъзможно да използваме директен подход, т.е. да имаме един масив от 109 елемента, в който да отбелязваме и следим цвета на всяко число от числовата ос. Подобна реализация би заела прекалено много памет и обхождането на масива би било много бавно.

Оптимизация на този подход обаче може да се направи много лесно, след като забележим следната особеност на крайния резултат: след последното боядисване получилата се числова ос ще бъде разделена на черно-бели сегменти с дължина по-голяма или равна на едно и краищата на тези сегменти винаги ще съвпадат с някой край на боядисване, т.е. с някое ai или bi. Важно е да се отбележи, че накрая не всяко ai или bi е на граница между бял и черен сегмент (в кода на C++ използвам turning point - точка на обръщане), но всички граници съвпадат с някое ai или bi.

След като сме забелязали тази особеност лесно се вижда оптимизацията, която можем да използваме: вместо да следим промените на цвета из цялата числова ос, ние се интересуваме само от сегментите, определени от краищата на оцветяванията ai, bi, 0 и 109, максималният брой на които е ≤ N + 2 ≤ 10002. Това вече е много по-приемлив обем на данни, с които можем да работим.

Алгоритъмът за решаването на задачата се състои следното:

Код на C++

#include <iostream>
#include <algorithm>

using namespace std;

int n;

int left_end[5010], right_end[5010]; // левите и десните краища на всички пребоядисани участъци
char color[5010]; // цвета на всички пребоядисани участъци

int tpoint_c = 2; // брояч
int tpoints[10020]; // (turning points) тук запазваме сортирани точките от числовата ос, в които е възможно да има два различни съседни цвята

bool segment[10020]; // масив, представляващ частите на които е разделена числовата ос от точките в tpoints, и съдържащ цвета на съответния сегмент (true = white, false = black)

// Функция за двоично търсене в масива tpoints
int binsearch (int val)
{
int l = 1, r = tpoint_c, mid;
// По построение ни е гарантирано, че търсената стойност съществува
while (true)
{
mid = (l + r)/2;
if (tpoints[mid] == val)
{
return mid;
}
else if (tpoints[mid] > val)
{
r = mid - 1;
}
else if (tpoints[mid] < val)
{
l = mid + 1;
}
}
}

int main()
{
// Инициализираме променливите
memset(tpoints, 0, sizeof(tpoints));
for(int i = 0; i <= 5001; i++) segment[i] = true;

cin >> n;

// Третираме първоначалното състояние просто като първото пребоядисване
tpoints[1] = left_end[1] = 0;
tpoints[2] = right_end[1] = 1000000000;
color[1] = 'w';
n++;

// Прочитаме входа и попълваме tpoints масива с крайните точки на пребоядисванията
for(int i = 2; i <= n ; i++)
{
cin >> left_end[i] >> right_end[i] >> color[i];
tpoints[++tpoint_c] = left_end[i];
tpoints[++tpoint_c] = right_end[i];
}

// сортираме масива, за да може по-късно да ползваме двоично търсене в него
sort((tpoints+1), (tpoints + tpoint_c + 1));


// За всеки интервал на пребоядисване взимаме лявата и дясна част, намираме къде са те спрямо другите краища в сортирания масив tpoints
// и след това оцветяваме съответния сегмент от числовата ос в този цвят
for(int i = 1; i <= n; i++)
{
int ll = binsearch(left_end[i]) + 1, rr = binsearch(right_end[i]);
for (int j = ll; j <= rr; j++)
{
segment[j] = (color[i] == 'w');
}
}

// След като всичко сме оцветили остава да намерим най-дългия бял сегмент от числовата ос
int end, len = 0, maxlen = 0;
for(int i = 2; i <= tpoint_c; i++)
{
if (segment[i])
{
len += tpoints[i] - tpoints[i - 1];
}
else
{
if (len > maxlen)
{
maxlen = len;
end = tpoints[i - 1];
}
len = 0;
}
}

if (len > maxlen)
{
maxlen = len;
end = tpoints[tpoint_c];
}

cout << (end - maxlen) << " " << end << endl;
}