Educational Round #2 — Разборы
Найдем наименьший и наибольший элемент массива. Затем вычислим сумму весов всех учеников, исключив тех, у кого вес равен наименьшему или наибольшему значению. Обозначим эту сумму , а количество таких учеников .
Остаётся вычислить средний вес учеников, разделив на , и вывести результат, округлённый до целого числа килограммов.
Объявим рабочий массив.
int w[1000];
Читаем входные данные. Количество входных чисел подсчитываем в переменной .
n = 0; while (scanf("%d", &w[n]) == 1) n++;
Находим наименьший и наибольший элемент массива.
min = max = w[0]; for (i = 0; i < n; i++) { if (w[i] > max) max = w[i]; if (w[i] < min) min = w[i]; }
Вычисляем сумму весов и количество учеников , исключив учеников с наибольшим и наименьшим весом.
s = cnt = 0; for (i = 0; i < n; i++) if (w[i] != min && w[i] != max) { s = s + w[i]; cnt++; }
Выводим ответ, округленный до целого числа килограммов.
printf("%.0lf\n", 1.0 * s / cnt);
Реализация алгоритма — min_element, max_element
Объявим рабочий массив.
vector<int> w;
Читаем входные данные.
while (scanf("%d", &x) == 1) w.push_back(x);
Находим наименьший и наибольший элемент массива.
min_el = *min_element(w.begin(), w.end()); max_el = *max_element(w.begin(), w.end());
Вычисляем сумму весов и количество учеников , исключив учеников с наибольшим и наименьшим весом.
s = cnt = 0; for (int x : w) if (x != min_el && x != max_el) { s += x; cnt++; }
Выводим ответ, округленный до целого числа килограммов.
printf("%.0lf\n", 1.0 * s / cnt);
Python реализация
import sys
Читаем входные данные.
lst = [] for line in sys.stdin: lst.extend(map(int,line.split()))
Находим наименьший и наибольший элемент списка.
max_el = max(lst) min_el = min(lst)
Вычисляем сумму весов и количество учеников , исключив учеников с наибольшим и наименьшим весом.
cnt = sum = 0 for x in lst: if x != max_el and x != min_el: cnt += 1 sum += x
Вычисляем и выводим ответ, округленный до целого числа килограммов.
res = round(sum / cnt) print(res)
Одна из сельскохозяйственных работ, которые фермер Джон не любит больше всего, — это таскать много коровьего навоза. Чтобы упростить этот процесс, он придумал гениальное изобретение: телепорт навоза! Вместо того, чтобы перевозить навоз между двумя точками в тележке позади трактора, он может использовать телепорт для навоза, чтобы мгновенно транспортировать навоз из одного места в другое.
Ферма Джона построена вдоль одной длинной прямой дороги, поэтому любое место на его ферме можно описать положением на этой дороге (точкой на числовой прямой). Телепорт описывается двумя числами и , где навоз, доставленный в точку , может быть мгновенно доставлен в точку , и наоборот.
Фермер Джон хочет транспортировать навоз из места в место , и он построил телепорт, который может быть полезен во время этого процесса (если телепорт не поможет, то его можно не использовать). Помогите ему определить минимальное общее расстояние, на которое следует перевезти навоз с помощью трактора.
Вход. Одна строка содержит четыре целых числа: и , описывающие начальную и конечную точки, за которыми следуют и , описывающие телепорт. Все позиции являются целыми числами в диапазоне , и они не обязательно отличаются друг от друга.
Выход. Выведите одно целое число — минимальное расстояние, на которое фермер Джон должен возить навоз на своем тракторе.
Пример. В этом примере лучшая стратегия — перетащить навоз из положения в положение , телепортировать его в положение , а затем перетащить в положение . Общее расстояние перевозки трактором составит .

3 10 8 2
3
У фермера Джона имеются следующие варианты передвижения навоза:
Напрямую от до , будет пройдено расстояние ;
Везти на тракторе от до , телепортировать от до , и снова везти на тракторе от до . Суммарно трактором будет пройдено расстояние ;
Везти на тракторе от до , телепортировать от до , и снова везти на тракторе от до . Суммарно трактором будет пройдено расстояние ;
Наименьшее из этих трех значений и будет ответом.
Пример
В приведенном примере лучшая стратегия — переместить навоз из позиции в позицию , телепортировать его в позицию , а затем переместить в позицию . Общее расстояние, которое проедет трактор, составит .
Читаем входные данные.
scanf("%d %d %d %d", &a, &b, &x, &y);
Вычисляем ответ — минимум среди трех значений.
res = abs(a - b); res = min(res, abs(a - x) + abs(b - y)); res = min(res, abs(a - y) + abs(b - x));
Выводим ответ.
printf("%d\n", res);
Найдите такое число от до включительно, для которого в разложении на простые множители количество множителей максимально. Если таких чисел несколько, выведите наибольшее из них.
Например, для ответом будет число , так как оно является наибольшим числом, разложение которого на простые множители включает два множителя: и .
Вход. Одно целое число .
Выход. Выведите одно искомое число.
7
6
Для того чтобы число, не превышающее , имело максимальное количество делителей, его следует представить в виде произведения как можно меньших простых чисел.
Например, попробуем искать его в виде . Найдем наибольшее , для которого . Количество делителей числа равно .
Затем рассмотрим число . Количество его делителей равно , что больше, чем .
Таким образом:
Если , то искомым числом будет ;
В противном случае ответом будет .
Отдельно рассмотрим случай . Для этого значения ответом будет .
Пример
Пусть . Наибольшей степенью двойки, не большей , будет . Число имеет делителей.
Теперь рассмотрим число . Количество его делителей равно:
Так как число не превышает , оно будет искомым для .
Читаем входное число .
scanf("%d",&n);
Ищем наибольшее , такое что .
p = 1; while (p * 2 <= n) p = p * 2;
Далее вычислим .
p1 = p / 2 * 3;
Находим ответ . Если , то ответом будет .
if (p1 <= n) res = p1; else res = p; if (n == 1) res = 1;
Выводим ответ.
printf("%d\n",res);
Вычислите значение функции
Вход. Два целых числа .
Выход. Выведите значение функции .
2 3
4
34 12
6
Реализуем рекурсивную функцию с запоминанием результатов.
Объявим двумерный массив для запоминания значений функции: .
long long dp[51][51];
Реализация рекурсивной функции f с запоминанием.
long long f(int x, int y) { if (x <= 0 || y <= 0) return 0; if (dp[x][y] != -1) return dp[x][y]; if (x <= y) return dp[x][y] = f(x - 1, y - 2) + f(x - 2, y - 1) + 2; return dp[x][y] = f(x - 2, y - 2) + 1; }
Основная часть программы. Читаем входные данные.
scanf("%d %d",&x,&y);
Инициализируем ячейки массива значением .
memset(dp,-1,sizeof(dp));
Вызываем функцию и выводим ее значение.
printf("%lld\n",f(x,y));
Однажды человек ( четное) встретились на площади и устроили два хоровода. Найдите количество способов, которыми человек могут водить два хоровода, если каждый хоровод должен состоять ровно из человек. Каждый человек должен принадлежать ровно к одному из этих двух хороводов.
Хоровод — танцевальный круг, состоящий из и более человек. Два хоровода называются неразличимыми (равными), если один можно преобразовать в другой выбором первого участника. Например, хороводы и неразличимы.
Вход. Одно четное целое число .
Выход. Выведите количество способов составить два хоровода. Гарантируется, что ответ помещается в -битный целочисленный тип данных.
Пример. Например, при количество способов равно :
первый раунд танца — , второй — ;
первый раунд танца — , второй — ;
первый раунд танца — , второй — .
4
3
Сначала следует выбрать людей для первого хоровода. Это можно сделать способами. Подсчитаем количество способов, которыми можно поставить людей внутри одного хоровода. Зафиксируем того кто будет первым, после чего остальных людей можно расставить произвольным образом, а именно способами. Количество способов составить два хоровода равно
Деление на необходимо чтобы не различать пары хороводов и . Например, при разделения на хороводы и будем считать одинаковыми.
Упростим указанную формулу и получим ответ:
Пример
Например, при количество способов равно :
первый раунд танца — , второй — ;
первый раунд танца — , второй — ;
первый раунд танца — , второй — .
По формуле для ответ равен
Построим все различимые хороводы для . На первом месте поставим участника . На остальных трех местах можно поставить любую перестановку из трех остальных участников.
Вращая по кругу любую из приведенных расстановок, можно получить неразличимые хороводы. Например, неразличимыми будут (рассмотрим циклические сдвиги последней перестановки):
Имеется хороводов из участников. Однако различимых хороводов для имеется .
Функция fact вычисляет факториал числа .
long long fact(int n) { long long res = 1; for (int i = 2; i <= n; i++) res *= i; return res; }
Основная часть программы. Читаем входное число .
scanf("%d", &n);
Вычисляем и выводим ответ.
res = 2 * fact(n - 1) / n; printf("%lld\n", res);
Вам дана матрица размером , в которой выделены специальных ячеек. Необходимо добраться из клетки в клетку . Разрешается двигаться только вправо или вниз.
Каждая из специальных ячеек обладает определённой силой. -я специальная ячейка имеет силу , и при прохождении через неё вы получаете эту силу.
Требуется найти суммарную силу, которую можно набрать, рассматривая все возможные пути от до .
Заметим, что:
Сила пути определяется как сумма значений всех специальных ячеек, посещённых на этом пути.
Обычные ячейки, не являющиеся специальными, имеют силу, равную нулю.
Вход. Первая строка содержит целое число — количество тестов.
В первой строке каждого теста заданы три целых числа и , где — размер сетки, а — количество специальных ячеек.
Каждая из следующих строк содержит три числа и , где — координаты специальной ячейки, а — её сила.
Выход. Для каждого теста выведите в отдельной строке суммарную силу, которую можно набрать. Так как ответ может быть очень большим, выведите его по модулю .
1 2 2 2 1 2 4 2 1 7
11
Пусть обозначает количество путей на матрице из клетки в клетку . Тогда
Количество путей из клетки в клетку , проходящих через специальную ячейку , равно . Умножив это число на , получаем суммарную силу всех путей, проходящих через ячейку .
Остается просуммировать силы всех путей, проходящих через специальные ячейки. Ответ будет равен сумме:
Пример
В приведенном примере заданы специальные ячейки.
Ровно один путь проходит через ячейку с силой .
Ровно один путь проходит через ячейку с силой .
Таким образом, общая сила равна .
Объявим массивы:
содержит факториалы чисел по модулю ,
содержит обратные значения к этим факториалам по модулю :
#define MAX 800001 ll fact[MAX], factinv[MAX];
Функция pow вычисляет степень числа по модулю: .
ll pow(ll x, ll n, ll p) { if (n == 0) return 1; if (n % 2 == 0) return pow((x * x) % p, n / 2, p); return (x * pow(x, n - 1, p)) % p; }
Функция inverse находит число, обратное к по простому модулю . Так как — простое число, то по малой теореме Ферма выполняется равенство:
Его можно переписать в виде , откуда следует, что обратным к является число .
ll inverse(ll x, ll p) { return pow(x, p - 2, p); }
Функция Cnk вычисляет биномиальный коэффициент по формуле:
ll Cnk(int n, int k) { return ((fact[n] * factinv[k]) % MOD * factinv[n - k]) % MOD; }
Функция ways вычисляет количество путей на решетке размером из клетки в клетку . Так как любой путь между этими точками имеет длину и содержит ровно горизонтальных сегментов, число путей равно .
ll ways(int n, int m) { return Cnk(n + m, m); }
Основная часть программы. Заполняем массивы факториалов и обратных факториалов .
fact[0] = 1; for (i = 1; i < MAX; i++) fact[i] = (fact[i - 1] * i) % MOD; factinv[0] = 1; for (i = 1; i < MAX; i++) factinv[i] = inverse(fact[i], MOD);
Последовательно обрабатываем тестов.
scanf("%d", &tests); while (tests--) {
Читаем данные текущего теста.
scanf("%d %d %d", &n, &m, &k);
Суммарную полученную силу сохраняем в переменной .
res = 0;
Обрабатываем специальных ячеек.
for (i = 0; i < k; i++) { scanf("%d %d %d", &x, &y, &p); res = (res + (ways(x - 1, y - 1) * ways(n - x, m - y)) % MOD * p) % MOD; }
Выводим ответ для текущего теста.
printf("%lld\n", res); }
В стране Смешариков — новый сезон! Теперь все герои отправляются в поход. Для этого им необходимо встретиться в одной точке, а уже оттуда двинуться покорять мир. Лосяш, координирующий действия Смешариков, знает координаты всех участников похода. Помогите ему определить, какое минимальное количество секунд потребуется, чтобы все Смешарики собрались вместе.
Изначально каждый Смешарик находится в узле целочисленной сетки. Если Смешарик стоит в точке , то за одну секунду он может переместиться в одну из точек , , , или остаться на месте.
Вход. В первой строке задано одно целое число — количество Смешариков. В следующих строках указаны их начальные позиции. Каждая позиция задаётся двумя целыми числами и .
Выход. Выведите минимальное количество секунд, необходимое для того, чтобы все Смешарики собрались в одной точке.
1 1 1
0
2 1 3 4 4
2
3 0 0 3 3 0 3
3
Пусть Смешарик находится в точке . Тогда за секунд он сможет оказаться в любой целочисленной точке ромба, как показано на рисунке.
Нужно найти наименьшее натуральное , при котором все ромбы соответствующего размера для каждого Смешарика будут иметь хотя бы одну общую целочисленную точку. Рассмотрим третий тест и изобразим ромбы для трёх Смешариков при .
При смешарики, например, могут встретиться в точке или .
Повернем систему координат на относительно начала координат. При таком преобразовании ромбы перейдут в квадраты. Выведем формулы поворота:
Или в декартовых координатах:
Избавимся от иррациональности, растянув изображение в раз. Для этого воспользуемся следующим преобразованием:
Преобразуем наши три точки:
Найдем координаты прямоугольника, содержащего все точки — преобразованные начальные координаты смешариков. Обозначим:
;
;
Тогда прямоугольник с противоположными углами и содержит все точки . При этом он является минимальным по площади прямоугольником, стороны которого параллельны осям координат.
В нашем примере таким прямоугольником будет .
Точкой сбора смешариков будет центр этого прямоугольника, то есть точка с координатами
Теперь нужно найти минимальное время, за которое все Смешарики смогут добраться из своих точек в точку . Для повернутого ромба (квадрата в новой системе координат) расстояние от точки до вычисляется по формуле:
Таким образом, все Смешарики смогут собраться в точке за время, равное максимальному из этих расстояний:
Объявим константу бесконечность .
typedef long long ll; #define INFL (ll)4e18
Входные точки будем хранить в массиве .
vector<pair<ll, ll>> inp;
Функция calc вычисляет расстояние от точки до самого удалённого смешарика. Преобразованные координаты смешариков находятся в массиве .
ll calc(ll x, ll y) { ll res = 0; for (auto p : inp) res = max(res, max(abs(p.first - x), abs(p.second - y))); return res; }
Основная часть программы. Читаем входные данные.
cin >> n; minx = INFL; maxx = -INFL; miny = INFL; maxy = -INFL; for (i = 0; i < n; i++) {
Читаем исходные координаты смешарика . Выполняем поворот координатной плоскости на и сохраняем полученные координаты в массиве .
cin >> x >> y; nx = x + y; ny = -x + y; inp.push_back({ nx, ny });
После этого находим координаты прямоугольника с противоположными углами и , который содержит все точки — преобразованные координаты смешариков.
minx = min(minx, nx); maxx = max(maxx, nx); miny = min(miny, ny); maxy = max(maxy, ny); }
Определяем точку сбора смешариков — центр прямоугольника.
resx = (minx + maxx) / 2; resy = (miny + maxy) / 2;
Вычисляем и выводим минимальное время, необходимое для того, чтобы все смешарики смогли добраться из своих точек до точки .
ans = calc(resx, resy); cout << ans << "\n";
Нурлашко, Нурбакыт и Жора — последние воины древнего клана ниндзя, ведущего борьбу против тирании императора Рэна. После сокрушительного поражения в открытом бою они решили разделить своё войско на три лагеря и перейти к партизанской войне.
Однако нелепые реформы императора Рэна установили строгие правила: по дорогам между городами можно двигаться только в одном направлении. Более того, направления были выбраны так, что невозможно, пройдя по нескольким дорогам, вернуться в исходный город.
Сейчас клан решает, где разместить свои лагеря. Армия императора совершает регулярные рейды, проверяя различные пути. Если в ходе одного такого рейда войска врага сумеют захватить все три лагеря, клан окажется неспособным перегруппироваться и проиграет войну. Ваша задача — помочь выбрать три города так, чтобы не существовало пути, проходящего через все эти города.
Вход. Первая строка содержит два числа — количество городов и дорог в империи. Далее следуют строк, каждая из которых содержит два числа , обозначающие направленную дорогу из города в город .
Выход. Выведите три числа — индексы городов, в которых клан должен разместить свои лагеря. Если подходящей тройки городов не существует, выведите . Если существует несколько решений, разрешается вывести любое из них.

3 2 1 2 2 3
-1
3 2 1 2 1 3
2 3 1
В графе требуется найти любые три вершины, которые не расположены на одном пути.
Для этого запустим алгоритм топологической сортировки Кана. Найдём две вершины, которые одновременно окажутся в очереди. В такой ситуации не существует пути, проходящего через обе эти вершины. Добавив к ним любую третью вершину, мы получим искомый ответ.
Если же при выполнении алгоритма Кана очередь на каждом шаге содержит не более одной вершины, это означает, что все вершины графа расположены на одном пути.
Пример
Графы из тестовых примеров имеют следующий вид:
В первом примере все три вершины лежат на одном пути.
Во втором примере существует три вершины, которые не находятся на одном пути.
Объявим структуры данных. Входной граф представлен списком смежности , а входящие степени вершин сохраняются в массиве .
vector<vector<int> > g; vector<int> InDegree; deque<int> q;
Читаем входные данные.
scanf("%d %d", &n, &m); g.resize(n + 1); InDegree.resize(n + 1); for (i = 0; i < m; i++) { scanf("%d %d", &a, &b); g[a].push_back(b);
Для каждого ребра увеличиваем значение на .
InDegree[b]++; }
Все вершины с нулевой входящей степенью помещаем в очередь .
for (i = 1; i < InDegree.size(); i++) if (InDegree[i] == 0) q.push_back(i);
Искомые три вершины сохраним в переменные .
x = y = -1;
Продолжаем выполнение алгоритма до тех пор, пока очередь не пуста.
while (!q.empty()) {
Если в очереди одновременно находится более одной вершины, это означает, что ответ найден.
if (q.size() > 1) { x = q[0]; y = q[1]; break; }
Извлекаем вершину из очереди — она станет текущей вершиной в порядке топологической сортировки.
v = q.front(); q.pop_front();
Удаляем из графа все ребра вида . Для каждого такого ребра уменьшаем входящую степень вершины . Если степень вершины становится нулевой, помещаем её в очередь.
for (int to : g[v]) { InDegree[to]--; if (InDegree[to] == 0) q.push_back(to); } }
Алгоритм Кана завершён. Если значение по-прежнему равно , это означает, что решения не существует.
if (x == -1) printf("-1\n"); else {
Если решение существует, в качестве выбираем минимальную вершину, отличную от и .
z = 1; while (z == x || z == y) z++; printf("%d %d %d\n", x, y, z); }
Фернандо был нанят Университетом Ватерлоо для завершения недавно начатого проекта. Университет планирует за пределами кампуса построить представительную улицу с бунгало для важных иностранных гостей и сотрудников.
В настоящее время улица застроена лишь частично: она начинается у берега озера и тянется к лесу, где и заканчивается. Задача Фернандо — завершить застройку улицы, построив ещё несколько бунгало в её конце, у леса. Все уже построенные дома расположены на одной стороне улицы, и новые бунгало должны быть размещены на той же стороне. Существующие бунгало различаются по типу и окрашены в разные цвета.
На взгляд Фернандо, улица выглядит немного хаотично. Он опасается, что новые дома, выполненные по его собственному дизайну, только усилят это впечатление. Чтобы внести элемент порядка, он решил подобрать такие цвета для новых бунгало, чтобы вся цветовая последовательность домов на улице стала симметричной. Это означает, что последовательность цветов должна читаться одинаково с обоих концов улицы.
Среди прочих задач Фернандо интересует, какое минимальное количество новых бунгало необходимо построить и покрасить соответствующим образом, чтобы завершить проект в соответствии с задуманной симметрией.
Вход. В первой строке задано целое число — количество уже построенных бунгало.
Во второй строке дана последовательность их цветов в порядке от начала улицы у озера. Это строка длиной , состоящая из строчных латинских букв от '' до '', каждая из которых обозначает определённый цвет.
Выход. Выведите минимальное количество бунгало, которые нужно добавить в конец улицы и покрасить соответствующим образом, чтобы вся цветовая последовательность стала симметричной.
3 abb
1
12 recakjenecep
11
15 murderforajarof
6
К данной строке необходимо добавить минимальное количество символов, чтобы она стала палиндромом. Для этого нужно найти наибольший палиндром среди всех её суффиксов, затем взять префикс до начала этого палиндрома, развернуть его и приписать к исходной строке.
Например, наибольшим палиндромным суффиксом строки является . Префикс до него — , переворачиваем его (получаем ) и приписываем к исходной строке.
Рассмотрим строку . Вычислим для нее -функцию.
Найдём наименьший индекс (такой, что ), для которого выполняется равенство . При этом значение будет максимальным среди таких индексов. Минимальное количество символов, которое необходимо дописать к исходной строке, чтобы получить палиндром, равно .
Для строки "" (длина ) нужный индекс , так как .
Пример
Рассмотрим первый пример. Вычислим -функцию для строки .
Наименьшим индексом , для которого выполняется равенство , является . Действительно: . Тогда ответ равен . То есть, к строке "" достаточно добавить один символ '', чтобы получить палиндром "".
Функция z строит и возвращает -функцию для строки .
vector<int> zfun(string s) { int i, l, r, len = s.size(); vector<int> z(len, 0); l = r = 0; for (i = 1; i < len; i++) { if (i <= r) z[i] = min(r - i + 1, z[i - l]); while (i + z[i] < len && s[z[i]] == s[i + z[i]]) z[i]++; if (i + z[i] - 1 > r) { l = i; r = i + z[i] - 1; } } return z; }
Основная часть программы. Читаем входную строку .
cin >> n; cin >> s;
Строим строку .
rs = s; reverse(rs.begin(), rs.end()); rs += s;
Вычисляем -функцию строки .
z = zfun(rs);
Находим наименьший индекс , для которого выполняется равенство . Среди всех таких индексов значение будет наибольшим.
for (i = n; i < rs.size(); i++) if (i + z[i] == 2 * n) {
Выводим ответ.
cout << n - z[i] << endl; break; }
Вдоль трассы Алматы–Тараз расположены nnn населённых пунктов, пронумерованных от до . С наступлением зимы неизвестных торговцев привезли из некого аула вязаные шапки и начали продавать их в этих населённых пунктах.
Торговцы следуют двум принципам:
Они не торгуют в одном месте более одного дня.
Каждый день они увеличивают цену на шапку.
Более формально каждый -ый торговец:
Начинает торговлю в населенном пункте со стартовой ценой за одну шапку.
Каждый день переходит в соседний населённый пункт: если накануне он торговал в пункте , то сегодня торгует в пункте .
Каждый день увеличивает цену на : если вчера цена составляла , то сегодня она равна .
Завершает торговлю в населённом пункте (при этом в он ещё продаёт).
Для каждого населённого пункта определите максимальную цену на одну шапку за всю историю торговли.
Вход. В первой строке заданы два целых числа и — количество населенных пунктов и количество торговцев соответственно.
Далее следуют строк, каждая из которых содержит три целых числа и — начальный и конечный населённые пункты, а также стартовая цена на шапку для -го торговца.
Выход. Выведите целых чисел, где -ое число обозначает максимальную цену на одну шапку за всю историю торговли в -ом населённом пункте. Если в каком-то пункте торговля не велась, выведите .
5 2 1 3 2 2 4 6
2 6 7 8 0
6 4 4 4 3 1 2 5 5 6 1 6 6 1
5 6 0 3 1 2
Построим дерево отрезков на диапазоне и изначально обнулим его. Пусть торговец продавал шапки в населенных пунктах , начиная с цены . Разобьем отрезок на фундаментальные отрезки . Тогда в вершину, соответствующую отрезку , занесем число
Например, пусть , и первая торговля охватывает города с номерами от до , начиная с цены . Тогда отрезок распадется на фундаментальные: . После обработки первой операции дерево отрезков будет выглядеть следующим образом:
Если фундаментальный отрезок содержит значение , это означает что:
торговец продавал шапки во всех городах от до .
в городе шапка продавалась по цене , в городе по цене , и так далее, вплоть до города , где её цена составляла .
Рассмотрим процедуру обработки запроса (отрезка, на котором торговец ведёт торговлю) на диапазоне , если начальная цена шапки равна .
В левом подотрезке торговец будет продавать шапки в городах, начиная с цены . В правом подотрезке первая цена шапки в городе, куда придёт торговец, составит . Таким образом, на отрезке с номерами торговля начнётся с цены . Однако если (что возможно, если весь отрезок запроса находится в правом подотрезке), следует положить .
Рассмотрим отрезок , у которого есть два подотрезка: и . Если в городе торговец продал шапку по цене , то в городе её цена составит .
В процессе обработки запросов реализуем проталкивание значений, поддерживающее максимум.
Для вывода ответа следует пройти по дереву, протолкнуть все значения до листов и вывести их.
Пример
Рассмотрим выполнение двух запросов, приведённых в примере. Разобьём отрезки запросов на фундаментальные:
;
.
Объявим массив для хранения дерева отрезков.
#define MAX 300010 long long SegTree[4*MAX];
Функция Push выполняет проталкивание значения из вершины в ее потомков.
void Push(int v, int lpos, int mid, int rpos) { if (SegTree[v]) { SegTree[2 * v] = max(SegTree[2 * v], SegTree[v]); SegTree[2 * v + 1] = max(SegTree[2 * v + 1], SegTree[v] + mid - lpos + 1); SegTree[v] = 0; } }
Функция Update выполняет продажу шапок на промежутке , начиная с цены .
void Update(int v, int lpos, int rpos, int left, int right, long long x) { if (left > right) return; if ((lpos == left) && (rpos == right)) SegTree[v] = max(SegTree[v], x); else { int mid = (lpos + rpos) / 2; Push(v, lpos, mid, rpos); int len = min(mid, right) - left + 1; if (len < 0) len = 0; Update(2 * v, lpos, mid, left, min(mid, right), x); Update(2 * v + 1, mid + 1, rpos, max(left, mid + 1), right, x + len); } }
Функция PrintResult проталкивает значения из внутренних вершин до листьев и выводит итоговое состояние массива.
void PrintResult(int v, int lpos, int rpos) { if (lpos == rpos) printf("%lld ", SegTree[v]); else { int mid = (lpos + rpos) / 2; Push(v, lpos, mid, rpos); PrintResult(2 * v, lpos, mid); PrintResult(2 * v + 1, mid + 1, rpos); } }
Основная часть программы. Читаем входные данные.
scanf("%d %d", &n, &m); memset(SegTree,0,sizeof(SegTree));
Обрабатываем запросов.
for(i = 0; i < m; i++) { scanf("%d %d %lld", &l, &r, &x); Update(1,1,n,l,r,x); }
Выводим результат.
PrintResult(1,1,n); printf("\n");