НОВОСТИ Разбираем реальные задачи для кандидатов в Яндекс

BDFINFO2.0
Оффлайн
Регистрация
14.05.16
Сообщения
11.398
Реакции
501
Репутация
0
5drhfost9i2nmn9onzzyiy_ixli.jpeg
Хабр, это снова я, Алексей Рак (фото не мое). В прошлом году, помимо основной работы, мне довелось стать одним из автором задач для кандидатов в Яндекс. Сегодня наша команда впервые за долгое время публикует на Хабре реальные задачи для разработчиков, которые устраиваются в компанию. Эти задачи использовались до февраля 2020 года при отборе на стажировку для бэкендеров. Решения проверял компьютер. Сейчас кандидатам достаются похожие задания.

Разборы и код сознательно спрятаны в спойлеры. Если вы интересуетесь или готовитесь к собеседованиям в большие IT-компании, попробуйте решить одну или несколько задач, прежде чем смотреть разбор. Код представлен на Python, C++ и Java.

Авторы и мои коллеги: задача A — Павел Дорошкевич, B и F — Егор Чунаев, E — Андрей Халявин, C и D — я.


 


 


 


 


 


 

A. День рождения Васи


Ограничение по времени на тест

3 с

Ограничение по памяти на тест

256 МБ

Ввод

стандартный ввод

Вывод

стандартный вывод
Вася и его друзья очень любят вкусно поесть. На свой день рождения каждый обязан удивить других, приготовив новые вкусные и полезные блюда.

Сегодня у Васи день рождения, и он позвал своих друзей отведать n своих самых лучших блюд. Название каждого из них состоит из строчных букв английского алфавита, цифр и знака подчеркивания.

Для приготовления блюда с номером i требуется z[SUB]i[/SUB] ингредиентов. Для каждого ингредиента известно его название, требуемое количество для одной порции блюда, а также единица измерения, в которой это количество задано. Помимо этого, Васе известно, что i-й кулинарный шедевр захотят попробовать c[SUB]i[/SUB] друзей.

Используются следующие единицы измерения:

  • g, kg — граммы и килограммы соответственно;
  • l, ml — литры и миллилитры соответственно;
  • cnt, tens — одна штука и десять штук соответственно.

В одном килограмме 1000 грамм и в одном литре 1000 миллилитров. Делать перевод из одной единицы измерения в другую можно тогда и только тогда, когда они одновременно обозначают или массу, или объем, или количество.

У Васи есть два справочника ингредиентов. В первом для каждого ингредиента указано его количество в упаковке и цена за упаковку. Во втором справочнике для каждого ингредиента указано содержание белков, жиров, углеводов и энергетическая ценность некоторого количества данного ингредиента.

Васе нужно приготовить все блюда, при этом не купить ничего лишнего. Для этого ему нужно определить, какие ингредиенты и в каком количестве необходимо приобрести в магазине.

Так как друзья именинника очень следят за питанием, то они, перед тем как попробовать Васины блюда, захотят узнать всё: как Вася готовил блюда, их энергетическую ценность и содержание белков, жиров и углеводов. Васе нужно подготовить эту информацию.

Необходимо помочь имениннику подсчитать, сколько требуется потратить денег на покупку продуктов в магазине, какие ингредиенты и в каком количестве нужно купить, а также для каждого блюда посчитать содержание белков, жиров, углеводов и энергетическую ценность, если его съесть полностью.

Входные данные


Первая строка содержит целое число n (1 ⩽ n ⩽ 1000) — количество блюд, которое решил приготовить Вася.

Затем следует описание n блюд. В первой строке содержатся строка d[SUB]i[/SUB] и целые числа с[SUB]i[/SUB], z[SUB]i[/SUB] (1 ⩽ с[SUB]i[/SUB], z[SUB]i[/SUB] ⩽ 100) — название блюда, количество друзей, желающих отведать данное блюдо, и количество ингредиентов необходимых для приготовления. Название блюда состоит из строчных букв английского алфавита, цифр и знака подчеркивания. Его длина не превосходит 20 символов.

В следующих z[SUB]i[/SUB] строках содержатся описания ингредиентов. В строке с номером j содержатся строка s[SUB]i, j[/SUB] — название ингредиента, целое число a[SUB]i, j[/SUB] (1 ⩽ a[SUB]i, j[/SUB] ⩽ 1000) — требуемое количество ингредиента и строка u[SUB]i, j[/SUB] — название единицы измерения количества. Название ингредиента состоит из строчных букв английского алфавита, цифр и знака подчеркивания. Длина строки не превосходит 20 символов.

Следующая строка содержит целое число k (1 ⩽ k ⩽ 1000) — количество ингредиентов в каталоге цен.

В каждой из следующих k строк содержатся четыре значения t[SUB]i[/SUB] p[SUB]i[/SUB] a[SUB]i[/SUB] u[SUB]i[/SUB], описывающих ингредиент.

  • t[SUB]i[/SUB] — название ингредиента, состоящее из строчных букв английского алфавита, цифр и знака подчеркивания. Длина строки не превосходит 20 символов;
  • p[SUB]i[/SUB] — стоимость ингредиента, заданная целым числом (1 ⩽ p[SUB]i[/SUB] ⩽ 1000);
  • a[SUB]i[/SUB] — количество ингредиента в упаковке в единицах, заданное целым числом (1 ⩽ a[SUB]i[/SUB] ⩽ 1000);
  • u[SUB]i[/SUB] — единица измерения количества (l, ml, g, kg, cnt или tens).

Следующая строка содержит число m (1 ⩽ m ⩽ 1000) — количество ингредиентов в каталоге еды.

Далее расположены m строк, в каждой из которых содержатся семь значений t[SUB]i[/SUB] a[SUB]i[/SUB] u[SUB]i[/SUB] pr[SUB]i[/SUB] f[SUB]i[/SUB] ch[SUB]i[/SUB] fv[SUB]i[/SUB], описывающих ингредиент.

  • t[SUB]i[/SUB] — название ингредиента, состоящее из строчных букв английского алфавита, цифр и знака подчеркивания. Длина строки не превосходит 20 символов;
  • a[SUB]i[/SUB] — количество ингредиента, для которого указаны характеристики ингредиента, заданное целым числом (1 ⩽ a[SUB]i[/SUB] ⩽ 1000);
  • u[SUB]i[/SUB] — единица измерения (l, ml, g, kg, cnt или tens);
  • pr[SUB]i[/SUB] f[SUB]i[/SUB] ch[SUB]i[/SUB] fv[SUB]i[/SUB] — содержание белков, жиров, углеводов и энергетическая ценность ингредиента соответственно, заданные вещественными числами с не более чем шестью знаками после запятой (0 ⩽ pr[SUB]i[/SUB], f[SUB]i[/SUB], ch[SUB]i[/SUB] ⩽ 1000, 0 ⩽ fv[SUB]i[/SUB] ⩽ 10 000).

Гарантируется, что:

  • в каталогах перечислены все ингредиенты, необходимые для приготовления блюд;
  • не существует двух блюд с одинаковым названием;
  • не существует двух ингредиентов в одном каталоге с одинаковым названием;
  • не существует двух ингредиентов в одном блюде с одинаковым названием;
  • для любых двух упоминаний ингредиента единицы измерения, в которых заданы его количества, можно перевести друг в друга.

Выходные данные


Первая строка должна содержать одно целое число: сумма, которую нужно потратить Васе на подготовку к празднику.

Далее должны следовать k строк, в каждой из которых через пробел указано название ингредиента и целое число — количество упаковок, которое необходимо купить. Ингредиенты, выведенные в этих k строках, должны соответствовать ингредиентам, описанным в первом справочнике.

В следующих n строках через пробел должны быть указаны название блюда и его характеристики, описанные четырьмя вещественными числами: белки, жиры, углеводы и энергетическая ценность.

Ингредиенты и блюда разрешается выводить в любом порядке.

Ваш ответ будет считаться правильным, если все целые числа совпадают с соответствующими числами в ответе жюри, а для всех вещественных чисел в ответе их абсолютная или относительная погрешность не превосходит 10[SUP]-3[/SUP]. Более формально, пусть вещественное число в вашем ответе равно A, а соответствующее число в ответе жюри равно B. Тогда число A будет считаться корректным, если
338394dde73e843636b06187fb287a0b.svg
.

Пример


Входные данные

Выходные данные

2
sandwich 7 3
butter 10 g
toasted_bread 2 cnt
sausage 30 g
omelet 9 4
egg 4 cnt
milk 120 ml
salt 1 g
sausage 50 g
7
egg 61 1 tens
milk 58 1 l
sausage 100 480 g
butter 120 180 g
cream 100 350 g
salt 14 1000 g
toasted_bread 40 20 cnt
8
egg 1 cnt 13 12 1 16.4
milk 1 l 3 4.5 4.7 60
chocolate 90 g 6.8 36.3 47.1 546
salt 1 kg 0 0 0 0
strawberry 100 g 0.4 0.1 7 35
sausage 100 g 10 18 1.5 210
toasted_bread 5 cnt 7.3 1.6 52.3 248
butter 100 g 0.8 72.5 1.3 661

734
egg 4
milk 2
sausage 2
butter 1
cream 0
salt 1
toasted_bread 1
sandwich 6.00 13.29 21.50 228.3
omelet 57.360 57.540 5.314 177.800
Примечание


В первом примере Васе необходимо приготовить 7 бутербродов и 9 омлетов.

Для приготовления всех первых блюд необходимо 10 ⋅ 7 грамм масла, 2 1 7 кусочков хлеба и 30 ⋅ 7 грамм колбасы. В каждом из бутерброде будет содержаться
9b6b5e9332644b6b3050a60769601a9b.svg
грамм белков,
16df0dfc2305bff874be8bbf7ca069fd.svg
грамм жиров и
2df864fd2a24b7e0a1791e770eac9848.svg
грамм углеводов. Энергетическаая ценность составит
ac6dfe301989eab9dedaefe53572fc2e.svg
.

Для приготовления всех вторых блюд необходимо 4 ⋅ 9 = 36 яиц, 120 ⋅ 9 = 1080 миллилитров молока, 9 грамм соли, 50 ⋅ 9 = 450 грамм колбасы. В каждом омлете будет содержаться
2b3f2d7e08795c316f6f1faa63adefc1.svg
грамм белков,
8a17c903525fc3495fb1cc382fae6e7a.svg
и
75eb5846f6480a6c143d2cef82e017d0.svg
углеводов. Энергетическая ценность составит
d4af51fcdd408a23d2ae7df8ae8e5322.svg
ккал.

Всего необходимо 70 грамм масла, 36 яиц, 1080 литров молока, 9 грамм соли, 210 + 450 = 660 грамм колбасы и 14 кусочков тостового хлеба.

Таким образом, в магазине нужно купить одну упаковку масла, 4 десятка яиц, 2 упаковки колбасы и молока, по одной упаковке соли и тостового хлеба, заплатив 120 + 61 ⋅ 4 + 100 ⋅ 2 + 58 ⋅ 2 + 14 + 40 = 734 рубля.

Решение
Итак, в задаче даны описания n блюд dishes, справочник с описанием цен prices для каждого из k ингредиентов, а также каталог catalog, где содержится описание количества белков, жиров и углеводов для m ингредиентов.

Для решения задачи нужно:

1. Определить, сколько требуется потратить денег на продукты, и указать минимальное количество каждого из них.
2. Вывести суммарное количество белков, жиров, углеводов и энергетическую ценность для каждого блюда.

Эти подзадачи можно решать независимо.

Опишем алгоритм для первой подзадачи. Нам нужны блюда dishes и справочник с описанием цен prices.

1. Заведем хэш-таблицу inredients, где ключ — название ингредиента, а значение — количество (в единицах измерения).
2. Для каждого блюда перебираем набор его ингредиентов.
3. Зная требуемое количество блюд и ингредиентов в блюде, добавляем в ingredients по ключу с названием ингредиента эту информацию.
4. Когда все блюда обработаны, для каждого ингредиента суммарное количество по всем блюдам делим на указанное количество в справочнике цен prices с округлением вверх.
5. Зная суммарное необходимое количество ингредиентов, легко найти итоговую сумму. Стоит иметь в виду, что для хранения суммарного количества денег 32-битного типа недостаточно.
6. Выводим сначала общее количество денег, а затем k строк с названием ингредиента и его количества.

Стоит обратить внимание, что если какой-то из ингредиентов не был использован ни разу, его все равно нужно вывести и указать число 0.

Опишем последовательность действий для второй подзадачи. Нам нужны блюда dishes и каталог с описанием ингредиентов catalog:

1. Для каждого блюда перебираем его набор ингредиентов.
2. Берем из каталога информацию об ингредиенте, умножаем на его количество в блюде и делим на количество, указанное в каталоге.
3. Прибавляем к ответу.
4. После обработки всех ингредиентов блюда выводим на экран.

Также стоит обратить особое внимание на работу с переводом величин. Так как единицы измерения в пределах одной группы кратны 10 ил 1000, можно использовать тип Decimal или целые числа. При использовании целых чисел стоит заранее умножить величины в описании блюд на 1000, так как при конвертации из граммов в килограммы и из миллилитров в литры необходимо деление. Так как перевод величин необходимо выполнять в нескольких местах, лучше всего вынести данную логику в функцию или класс.

Тип данных float плохо подходит для хранения количества ингредиента, так как имеет точность всего лишь в 6-7 десятичных знаков и требует обрабатывать ошибки округления. Точности типа данных double достаточно для выполнения операций и вывода результатов. Его можно использовать, если корректно делать округление.

Асимптотическая сложность алгоритма при использовании хэш-таблиц для каталога и справочника:
926584d9857acd5a8ca62ffcc3370286.svg
, где z[SUB]i[/SUB] — количество ингредиентов в i-м блюде.

Код на Python

import collections
import math
import typing
from decimal import Decimal


class Amount(object):
def __init__(self, count: Decimal, unit: str):
self.count = count
self.unit = unit

def convert_to(self, unit: str):
multipliers_dict = {
'g': 1,
'kg': 1000,
'ml': 1,
'l': 1000,
'cnt': 1,
'tens': 10
}
assert self.is_compatible(self.unit, unit)

return Amount(self.count * multipliers_dict[self.unit] / multipliers_dict[unit], unit)

@staticmethod
def is_compatible(unit1, unit2):
unit_classes = [
('g', 'kg'),
('ml', 'l'),
('cnt', 'tens')
]

for unit_class in unit_classes:
if unit1 in unit_class:
return unit2 in unit_class
return False


class FoodInfo(object):
def __init__(self, proteins: Decimal = Decimal(0), fats: Decimal = Decimal(0), carbohydrates: Decimal = Decimal(0), food_value: Decimal = Decimal(0)):
self.proteins = proteins
self.fats = fats
self.carbohydrates = carbohydrates
self.food_value = food_value

def __mul__(self, other: Decimal):
assert isinstance(other, Decimal)

return FoodInfo(self.proteins * other, self.fats * other, self.carbohydrates * other, self.food_value * other)

def __add__(self, other):
assert isinstance(other, FoodInfo)

return FoodInfo(
self.proteins + other.proteins,
self.fats + other.fats,
self.carbohydrates + other.carbohydrates,
self.food_value + other.food_value
)


class AmountFoodInfo(object):
def __init__(self, amount, food_info):
self.amount = amount
self.food_info = food_info


class Ingredient(object):
def __init__(self, name: str, amount: Amount):
self.name = name
self.amount = amount


class CatalogIngredientInfo(object):
def __init__(self, name: str, price: int, amount: Amount):
self.name = name
self.price = price
self.amount = amount


class Dish(object):
def __init__(self, name: str, count: int, ingredients: typing.List[Ingredient]):
self.name = name
self.count = count
self.ingredients = ingredients


def read_dishes():
dish_count = int(input())

dishes = []
for _ in range(dish_count):
dish_name, dish_count, ingredient_count = input().split(' ')
ingredient_count = int(ingredient_count)

ingredients = []
for _ in range(ingredient_count):
ingredient_name, amount, unit = input().split(' ')
ingredients.append(Ingredient(name=ingredient_name, amount=Amount(Decimal(amount), unit)))

dishes.append(Dish(name=dish_name, ingredients=ingredients, count=int(dish_count)))

return dishes


def read_catalog():
ingredient_count = int(input())

catalog = {}
for _ in range(ingredient_count):
name, price, amount, unit = input().split(' ')
catalog[name] = CatalogIngredientInfo(
name=name,
price=int(price),
amount=Amount(Decimal(amount), unit)
)

return catalog


def read_food_info():
info_count = int(input())

food_info = {}
for _ in range(info_count):
name, amount, unit, proteins, fats, carbohydrates, food_value = input().split(' ')
food_info[name] = AmountFoodInfo(
amount=Amount(
count=Decimal(amount),
unit=unit
),
food_info=FoodInfo(
proteins=Decimal(proteins),
fats=Decimal(fats),
carbohydrates=Decimal(carbohydrates),
food_value=Decimal(food_value)
)
)

return food_info


def main():
dishes = read_dishes()
catalog = read_catalog()
food_info = read_food_info()

need_ingredients = collections.defaultdict(Decimal)
need_ingredients_count = collections.defaultdict(int)
dish_info = collections.defaultdict(FoodInfo)
for dish in dishes:
for ingredient in dish.ingredients:
converted_amount = ingredient.amount.convert_to(catalog[ingredient.name].amount.unit)
converted_food_info_amount = food_info[ingredient.name].amount.convert_to(catalog[ingredient.name].amount.unit)
need_ingredients[ingredient.name] += converted_amount.count * dish.count
dish_info[dish.name] += food_info[ingredient.name].food_info * (converted_amount.count / converted_food_info_amount.count)

total_price = Decimal(0)
for ingredient_name, need_ingredient in need_ingredients.items():
need_count = need_ingredient // catalog[ingredient_name].amount.count
if need_count * catalog[ingredient_name].amount.count < need_ingredient:
need_count += 1

need_ingredients_count[ingredient_name] = need_count
total_price += catalog[ingredient_name].price * need_count

print(total_price)
for name in catalog.keys():
print(name, need_ingredients_count[name])

for name, info in dish_info.items():
print(name, info.proteins, info.fats, info.carbohydrates, info.food_value)


if __name__ == '__main__':
main()

Код на C++

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#pragma comment(linker, "/STACK:256000000")

using namespace std;

typedef long long int ll;
typedef long double ld;

const int INF = 1000 * 1000 * 1000 + 21;
const ll LLINF = (1ll > igs;
ld arr[4] = {0.0, 0.0, 0.0, 0.0};
};

int n, k, m;
char buf[MAX_LEN];
char buf_cnt[MAX_LEN];
rs arr[MAX_N];
unordered_map info;

ll get_cnt() {
ll cnt;
scanf("%lld %s", &cnt, buf_cnt);
if (!strcmp(buf_cnt, "kg") || !strcmp(buf_cnt, "l")) {
return 1000 * cnt;
}
if (!strcmp(buf_cnt, "tens")) {
return 10 * cnt;
}
return cnt;
}

int main() {
#ifdef CH_EGOR
freopen("input.txt", "r", stdin);
//freopen("output.txt", "w", stdout);
#else
//freopen("", "r", stdin);
//freopen("", "w", stdout);
#endif

scanf("%d", &n);
for (int i = 0; i < n; ++i) {
int sz;
scanf("%s%lld%d", buf, &arr.mult, &sz);
arr.name = buf;
arr.igs.resize(sz);
for (int j = 0; j < sz; ++j) {
scanf("%s", buf);
arr.igs[j].first = buf;
arr.igs[j].second = get_cnt();
}
}

scanf("%d", &k);
for (int i = 0; i < k; ++i) {
ll cost;
scanf("%s%lld", buf, &cost);
ig_info& cur_info = info[buf];
cur_info.cost = cost;
cur_info.cnt = get_cnt();
}

scanf("%d", &m);
for (int i = 0; i < m; ++i) {
scanf("%s", buf);
ig_info& cur_info = info[buf];
cur_info.eg_cnt = get_cnt();
for (int j = 0; j < 4; ++j) {
double x;
scanf("%lf", &x);
cur_info.arr[j] = x;
}
if (cur_info.cnt == 0) {
info.erase(buf);
}
}

for (int i = 0; i < n; ++i) {
for (int j = 0; j < (int)arr.igs.size(); ++j) {
ig_info& cur_info = info[arr.igs[j].first];
cur_info.buy_cnt += arr.mult * arr.igs[j].second;
for (int k = 0; k < 4; ++k) {
arr.arr[k] += ((double)arr.igs[j].second / (double)cur_info.eg_cnt) * cur_info.arr[k];
}
}
}

ll ans = 0;
for (auto& cur_info : info) {
cur_info.second.buy_cnt = (cur_info.second.buy_cnt + cur_info.second.cnt - 1) / cur_info.second.cnt;
ans += cur_info.second.buy_cnt * cur_info.second.cost;
}

printf("%lld\n", ans);

for (const auto& cur_info : info) {
printf("%s %lld\n", cur_info.first.c_str(), cur_info.second.buy_cnt);
}

for (int i = 0; i < n; ++i) {
printf("%s ", arr.name.c_str());
for (int j = 0; j < 4; ++j) {
printf("%.20lf%c", (double)arr.arr[j], " \n"[j == 3]);
}
}

return 0;
}

Код на Java

import java.io_OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io_OutputStream;
import java.io.PrintWriter;
import java.io.BufferedWriter;
import java.util.HashMap;
import java.io.IOException;
import java.util.Map.Entry;
import java.io.Writer;
import java.io_OutputStreamWriter;
import java.io.InputStream;

/**
* Built using CHelper plug-in
* Actual solution is at the top
*
* @author ch_egor
*/
public class Main {
public static void main(String[] args) {
InputStream inputStream = System.in;
OutputStream outputStream = System.out;
InputReader in = new InputReader(inputStream);
OutputWriter out = new OutputWriter(outputStream);
Recipes solver = new Recipes();
solver.solve(1, in, out);
out.close();
}

static class Recipes {
static final int CNT_ENERGY = 4;

public void solve(int testNumber, InputReader in, OutputWriter out) {
int n = in.nextInt();

Recipes.Recipe[] recipes = new Recipes.Recipe[n];
for (int i = 0; i < n; ++i) {
recipes = new Recipes.Recipe();
recipes.name = in.nextString();
recipes.mult = in.nextLong();

int ingredientsSize = in.nextInt();
recipes.ingredients = new Recipes.Ingredient[ingredientsSize];
for (int j = 0; j < recipes.ingredients.length; ++j) {
recipes.ingredients[j] = new Recipes.Ingredient();
recipes.ingredients[j].name = in.nextString();
recipes.ingredients[j].cnt = readCnt(in);
}
}

int k = in.nextInt();
HashMap ingredientBuyInfo = new HashMap<>();
for (int i = 0; i < k; ++i) {
String name = in.nextString();

Recipes.IngredientBuyInfo curBuyInfo = new Recipes.IngredientBuyInfo();
curBuyInfo.cost = in.nextLong();
curBuyInfo.cnt = readCnt(in);

ingredientBuyInfo.put(name, curBuyInfo);
}

int m = in.nextInt();
HashMap ingredientEnergyInfo = new HashMap<>();
for (int i = 0; i < m; ++i) {
String name = in.nextString();

Recipes.IngredientEnergyInfo curEnergyInfo = new Recipes.IngredientEnergyInfo();
curEnergyInfo.cnt = readCnt(in);
for (int j = 0; j < CNT_ENERGY; ++j) {
curEnergyInfo.energyArr[j] = in.nextDouble();
}

ingredientEnergyInfo.put(name, curEnergyInfo);
}

for (int i = 0; i < n; ++i) {
for (int j = 0; j < recipes.ingredients.length; ++j) {
Recipes.IngredientBuyInfo curBuyInfo = ingredientBuyInfo.get(recipes.ingredients[j].name);
curBuyInfo.buyCnt += recipes.mult * recipes.ingredients[j].cnt;

Recipes.IngredientEnergyInfo curEnergyInfo = ingredientEnergyInfo.get(recipes.ingredients[j].name);
for (int p = 0; p < CNT_ENERGY; ++p) {
recipes.energyArr[p] += ((double) recipes.ingredients[j].cnt / (double) curEnergyInfo.cnt) * curEnergyInfo.energyArr[p];
}
}
}

long totalCost = 0;
for (HashMap.Entry entry : ingredientBuyInfo.entrySet()) {
totalCost += entry.getValue().cost * ((entry.getValue().buyCnt + entry.getValue().cnt - 1) / entry.getValue().cnt);
}

out.println(totalCost);

for (HashMap.Entry entry : ingredientBuyInfo.entrySet()) {
out.println(entry.getKey() + " " + ((entry.getValue().buyCnt + entry.getValue().cnt - 1) / entry.getValue().cnt));
}

for (int i = 0; i < n; ++i) {
out.print(recipes.name + " ");
for (int j = 0; j < CNT_ENERGY; ++j) {
out.print(String.format("%.20f", recipes.energyArr[j]) + (j == CNT_ENERGY - 1 ? "\n" : " "));
}
}
}

private long readCnt(InputReader in) {
int cnt = in.nextInt();
String type = in.nextString();
if (type.compareTo("kg") == 0 || type.compareTo("l") == 0) {
return 1000 * cnt;
}
if (type.compareTo("tens") == 0) {
return 10 * cnt;
}
return cnt;
}

static class Ingredient {
String name;
long cnt;

}

static class IngredientBuyInfo {
long cost;
long cnt;
long buyCnt;

}

static class IngredientEnergyInfo {
long cnt;
double[] energyArr;

IngredientEnergyInfo() {
energyArr = new double[CNT_ENERGY];
for (int i = 0; i < CNT_ENERGY; ++i) {
energyArr = 0.0;
}
}

}

static class Recipe {
String name;
long mult;
Recipes.Ingredient[] ingredients;
double[] energyArr;

Recipe() {
energyArr = new double[CNT_ENERGY];
for (int i = 0; i < CNT_ENERGY; ++i) {
energyArr = 0.0;
}
}

}

}

static class InputReader {
private InputStream stream;
private byte[] buf = new byte[1024];
private int curChar;
private int numChars;
private InputReader.SpaceCharFilter filter;

public InputReader(InputStream stream) {
this.stream = stream;
}

public int read() {
if (numChars == -1) {
throw new UnknownError();
}
if (curChar >= numChars) {
curChar = 0;
try {
numChars = stream.read(buf);
} catch (IOException e) {
throw new UnknownError();
}
if (numChars '9') {
throw new UnknownError();
}
res *= 10;
res += c - '0';
c = read();
} while (!isSpaceChar(c));
return res * sgn;
}

public long nextLong() {
int c = read();
while (isSpaceChar(c)) {
c = read();
}
int sgn = 1;
if (c == '-') {
sgn = -1;
c = read();
}
long res = 0;
do {
if (c < '0' || c > '9') {
throw new UnknownError();
}
res *= 10;
res += c - '0';
c = read();
} while (!isSpaceChar(c));
return res * sgn;
}

public String nextString() {
int c = read();
while (isSpaceChar(c)) {
c = read();
}
StringBuilder res = new StringBuilder();
do {
if (Character.isValidCodePoint(c)) {
res.appendCodePoint(c);
}
c = read();
} while (!isSpaceChar(c));
return res.toString();
}

public boolean isSpaceChar(int c) {
if (filter != null) {
return filter.isSpaceChar(c);
}
return isWhitespace(c);
}

public static boolean isWhitespace(int c) {
return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
}

public double nextDouble() {
int c = read();
while (isSpaceChar(c)) {
c = read();
}
int sgn = 1;
if (c == '-') {
sgn = -1;
c = read();
}
double res = 0;
while (!isSpaceChar(c) && c != '.') {
if (c == 'e' || c == 'E') {
return res * Math.pow(10, nextInt());
}
if (c < '0' || c > '9') {
throw new UnknownError();
}
res *= 10;
res += c - '0';
c = read();
}
if (c == '.') {
c = read();
double m = 1;
while (!isSpaceChar(c)) {
if (c == 'e' || c == 'E') {
return res * Math.pow(10, nextInt());
}
if (c < '0' || c > '9') {
throw new UnknownError();
}
m /= 10;
res += (c - '0') * m;
c = read();
}
}
return res * sgn;
}

public interface SpaceCharFilter {
public boolean isSpaceChar(int ch);

}

}

static class OutputWriter {
private final PrintWriter writer;

public OutputWriter(OutputStream outputStream) {
writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(outputStream)));
}

public OutputWriter(Writer writer) {
this.writer = new PrintWriter(writer);
}

public void print(Object... objects) {
for (int i = 0; i < objects.length; i++) {
if (i != 0) {
writer.print(' ');
}
writer.print(objects);
}
}

public void println(Object... objects) {
print(objects);
writer.println();
}

public void close() {
writer.close();
}

public void println(long i) {
writer.println(i);
}

}
}

B. Закрытый ключ


Ограничение по времени на тест

2 с

Ограничение по памяти на тест

256 МБ

Ввод

стандартный ввод

Вывод

стандартный вывод

Во всех крупных IT-компаниях немалое внимание уделяется вопросам информационной безопасности, и Яндекс — не исключение.


Дима и Егор разрабатывают новый сервис YD (Yandex Dorogi) и в данный момент проводят аудит его безопасности. Для шифрования пользовательских данных в YD используется алгоритм шифрования с открытым ключом YS (Yandex Shifrovatel).


Схема работы YS такова: для каждого сервиса генерируется закрытый ключ (
6fef16813dcf881cb6c1aa95c79388e4.svg
), где
839f25c2746382debd4f08ea25ad5ecf.svg
и
d68cc4926bf74bae8fa3b51ca4a09ec8.svg
— натуральные числа. По закрытому ключу (
6fef16813dcf881cb6c1aa95c79388e4.svg
) генерируется открытый ключ (НОД(
6fef16813dcf881cb6c1aa95c79388e4.svg
), НОК(
6fef16813dcf881cb6c1aa95c79388e4.svg
)), который доступен всем пользователям. Если злоумышленник сможет по открытому ключу получить закрытый ключ, то он получит доступ ко всем данным YD и принесет сервису непоправимый вред. Конечно же, Егор и Дима не могут этого допустить, поэтому они хотят сделать так, чтобы злоумышленнику пришлось перебрать очень много вариантов открытого ключа, прежде чем он сможет его угадать.


Дима уже сгенерировал закрытый ключ для YD и получил на его основе открытый ключ (
ca56859a9c0a213789bc5f4b3a866965.svg
). Егору сразу же стало интересно, сколько вариантов закрытого ключа придется перебрать злоумышленнику для взлома YD в худшем случае, иными словами, сколько существует закрытых ключей (
6fef16813dcf881cb6c1aa95c79388e4.svg
) таких, что открытым ключом для них является (
ca56859a9c0a213789bc5f4b3a866965.svg
). К сожалению, у Егора есть много других задач, очень важных для запуска YD, поэтому он просит вас вычислить это количество за него.



Входные данные



В первой строке содержатся два целых числа
817b92407f764f57af9226e50cc788fd.svg
и
9b34c4da5c757d4982bbd1b6f2e8998a.svg
(
2cd0f261487ff6566304d2c3a3968eee.svg
) — описание открытого ключа.



Выходные данные



Выведите одно целое число — количество закрытых ключей, для которых данный ключ является открытым.



Примеры



Входные данные

5 10

Выходные данные
2


Входные данные
10 11

Выходные данные
0


Входные данные
527 9486

Выходные данные
4




Примечание



В первом примере существует два закрытых ключа, для которых (
693c8e0a335da1266ebbc86e87ad1421.svg
) является открытым ключом: (
693c8e0a335da1266ebbc86e87ad1421.svg
) и (
8fe7e59e81a2f9d3bcbe28832475e5d4.svg
).


Во втором примере Дима ошибся, потому что ни один закрытый ключ не порождает открытый ключ (
f8839526d0658c5efe55c22359ac116c.svg
).


В третьем примере подходящими закрытыми ключами являются (
5ec2a90871be5fe341da92e379b127c7.svg
), (
369130557faaaa01be79bdf388fac2bd.svg
), (
c6769cb11aca76da9ff2d4b28bef069c.svg
), (
5502ef25a74348f97d0fba12475cf9ee.svg
).


НОД (наибольшим общим делителем) двух натуральных чисел
839f25c2746382debd4f08ea25ad5ecf.svg
и
d68cc4926bf74bae8fa3b51ca4a09ec8.svg
называется наибольшее число
16da507b2fc389688ef0659939dcc647.svg
такое, что
839f25c2746382debd4f08ea25ad5ecf.svg
делится на
16da507b2fc389688ef0659939dcc647.svg
и
d68cc4926bf74bae8fa3b51ca4a09ec8.svg
делится на
16da507b2fc389688ef0659939dcc647.svg
. Например, НОД(
62d771be0f50d3a019c5a4889b22d326.svg
) равен
3e328ec6538d48db0bbd8e0859e6d10a.svg
, а НОД(
ff7b47ee1bad37c9f279aa36bba97d01.svg
) равен
dc05370a7a9b97e8f4723f5a23022461.svg
.


НОК (наименьшим общим кратным) двух натуральных чисел
839f25c2746382debd4f08ea25ad5ecf.svg
и
d68cc4926bf74bae8fa3b51ca4a09ec8.svg
называется наименьшее число
16da507b2fc389688ef0659939dcc647.svg
такое, что
16da507b2fc389688ef0659939dcc647.svg
делится на
839f25c2746382debd4f08ea25ad5ecf.svg
и
16da507b2fc389688ef0659939dcc647.svg
делится на
d68cc4926bf74bae8fa3b51ca4a09ec8.svg
. Например, НОК(
fdb28693faa2ecf3ab9469020660bbd1.svg
) равен
a9805b3422e51c726fc4b1931f2d51aa.svg
, а НОК(
5c2471c627e32c5d89b242973880ec27.svg
) равен 20.

Решение и код

Если
9b34c4da5c757d4982bbd1b6f2e8998a.svg
не делится на
817b92407f764f57af9226e50cc788fd.svg
, то ответ, очевидно, равен нулю.


Иначе рассмотрим какую-то пару
839f25c2746382debd4f08ea25ad5ecf.svg
,
d68cc4926bf74bae8fa3b51ca4a09ec8.svg
, что
13655759335015ef41160b1dd5e5adca.svg
, а
88806a4bb2e94edbf0367579bb539cfd.svg
. С каждой такой парой можно сопоставить два числа
e483f4ca9756a62cab32a18394afb4a0.svg
и
1c7d11aae113f2634be259b7e8800705.svg
такие, что
91a27ea70021f12e4aea2993abb6f3eb.svg
, а
15230d180523b930e29ecd95102977de.svg
.


Иными словами, задачу можно свести к задаче нахождения количества пар взаимно простых чисел
826cecefefae504f9e79954dc80e110d.svg
,
126f7da4aefaf17991f550dcd9be0ede.svg
таких, что их
1d4b37d590fb86008d4cbe27e3dfec70.svg
равен
5b42e4e2f0ce4fac883639e09af6a8d5.svg
.


Далее будем предполагать что
0b29963b000c32b3e30f3a9b7dab1fac.svg
, если это не так, то сводим исходную задачу к задаче
c4e49f82da32be126993ed787af15df0.svg
,
4fe21d2b75439a9f947a090f106c0519.svg
.

Первый вариант решения


Решение за
f608bbb88837f81b681dad8c59b8992e.svg
.


Зафиксируем
826cecefefae504f9e79954dc80e110d.svg
, тогда ему в пару может подойти только
2567c4ae9006d493d48cd8b031bf9e41.svg
, поскольку
06c0187a7dc26e48c03f1c31f17daa32.svg
. Единственное, что надо проверить, это равенство
e6daef9b906808ad0afd7500a1f81a4a.svg
, это можно сделать алгоритмом Евклида за
523f9e0213b24efa3fbb6e0a3115cc97.svg
.


Под факторизацией подразумевается любой способ нахождения делителей. С учетом достаточно маленьких ограничений их можно найти просто проверкой всех целых чисел от
3e4c77a6e7c579a778fa84a18b6f4be0.svg
до
fbb32386fe31a5d9e08e01e8c0447ae4.svg
, что имеет сложность
f3bf896ef4ec462d2632527fca3328f8.svg
. Известно, что количество делителей точно не превосходит
d40c385b0face6a2027a26f0999125bf.svg
, так что проверку, что
e6daef9b906808ad0afd7500a1f81a4a.svg
, нужно выполнить не более
d40c385b0face6a2027a26f0999125bf.svg
раз, что имеет сложность
599aca1022ad5433c1ba4d24f6c675fb.svg
.


Получаем решение за
08c70b215c0c2b9431a1cd46523a8104.svg
.

Код на С++

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#pragma comment(linker, "/STACK:256000000")

using namespace std;

typedef long long int ll;
typedef long double ld;

const int INF = 1000 * 1000 * 1000 + 21;
const ll LLINF = (1ll /freopen("output.txt", "w", stdout);
#else
//freopen("", "r", stdin);
//freopen("", "w", stdout);
#endif

scanf("%lld%lld", &x, &y);

if (y % x != 0) {
return 0 * printf("0\n");
}

y /= x;

ll ans = 0;
for (ll i = 1; i * i i) == 1) {
ans += 1 + (i * i != y);
}
}
}

printf("%lld\n", ans);

return 0;
}

Второй вариант решения


Решение за
52ab7c4b32a33cd884d957018caad7ca.svg
.


Рассмотрим простое число
4ec3e23638b6073b649999485c251c94.svg
, на которое делится
8f01cec44bfb67a87f4cbee549b21420.svg
. Если
046ac36539ed958df0f9578605aa6fb0.svg
, то либо
826cecefefae504f9e79954dc80e110d.svg
, либо
126f7da4aefaf17991f550dcd9be0ede.svg
должно делится на
7e51c357f3ada0c7683156fb6df630e3.svg
, где
7234a52ba041cdb09b9328a047048fb2.svg
— максимальная степень, с которой
4ec3e23638b6073b649999485c251c94.svg
входит в
8f01cec44bfb67a87f4cbee549b21420.svg
. Чтобы выполнялось
91a27ea70021f12e4aea2993abb6f3eb.svg
, одно из этих чисел не должно делиться на
4ec3e23638b6073b649999485c251c94.svg
.


Иными словами, для каждого простого числа, на которое делится
8f01cec44bfb67a87f4cbee549b21420.svg
, мы выбираем, пойдет оно в
826cecefefae504f9e79954dc80e110d.svg
или в
126f7da4aefaf17991f550dcd9be0ede.svg
. Тогда ответ на задачу равен
2c81279e920ba54597c20e0cf5f1efde.svg
.


Код на Python

x, y = map(int, input().split())

if y % x != 0:
print(0)
else:
y //= x

i = 2
ans = 0
while i * i /= i
i += 1

if y != 1:
ans += 1

print(2 ** ans)

Код на C++

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#pragma comment(linker, "/STACK:256000000")

using namespace std;

typedef long long int ll;
typedef long double ld;

const int INF = 1000 * 1000 * 1000 + 21;
const ll LLINF = (1ll /freopen("output.txt", "w", stdout);
#else
//freopen("", "r", stdin);
//freopen("", "w", stdout);
#endif

scanf("%lld%lld", &x, &y);

if (y % x != 0) {
return 0 * printf("0\n");
}

y /= x;

ll ans = 0;
for (ll i = 2; i * i = i;
}
}
}
ans += (y != 1);

printf("%lld\n", (1ll code>

Код на Java

import java.io_OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io_OutputStream;
import java.io.PrintWriter;
import java.io.BufferedWriter;
import java.io.Writer;
import java.io_OutputStreamWriter;
import java.io.IOException;
import java.io.InputStream;

/**
* Built using CHelper plug-in
* Actual solution is at the top
*
* @author ch_egor
*/
public class Main {
public static void main(String[] args) {
InputStream inputStream = System.in;
OutputStream outputStream = System.out;
InputReader in = new InputReader(inputStream);
OutputWriter out = new OutputWriter(outputStream);
GcdAndLcm solver = new GcdAndLcm();
solver.solve(1, in, out);
out.close();
}

static class GcdAndLcm {
public void solve(int testNumber, InputReader in, OutputWriter out) {
long x = in.nextLong();
long y = in.nextLong();

if (y % x != 0) {
out.println(0);
return;
}
y /= x;

long ans = 0;
for (long i = 2; i * i = i;
}
}
}
if (y != 1) {
++ans;
}

out.println(1L = numChars) {
curChar = 0;
try {
numChars = stream.read(buf);
} catch (IOException e) {
throw new UnknownError();
}
if (numChars '9') {
throw new UnknownError();
}
res *= 10;
res += c - '0';
c = read();
} while (!isSpaceChar(c));
return res * sgn;
}

public boolean isSpaceChar(int c) {
if (filter != null) {
return filter.isSpaceChar(c);
}
return isWhitespace(c);
}

public static boolean isWhitespace(int c) {
return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
}

public interface SpaceCharFilter {
public boolean isSpaceChar(int ch);

}

}

static class OutputWriter {
private final PrintWriter writer;

public OutputWriter(OutputStream outputStream) {
writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(outputStream)));
}

public OutputWriter(Writer writer) {
this.writer = new PrintWriter(writer);
}

public void close() {
writer.close();
}

public void println(long i) {
writer.println(i);
}

public void println(int i) {
writer.println(i);
}

}
}

C. Программист на пляже


Ограничение по времени на тест

2 с

Ограничение по памяти на тест

256 МБ

Ввод

стандартный ввод

Вывод

стандартный вывод

Однажды программист Алексей из Яндекса взял отпуск и уехал отдыхать на море. В один из дней он пошел на пляж, причем пошел туда не один. Возможно, он пошел туда с мамой, возможно, с бабушкой, а, возможно, с другом или подругой. Важно, что пошел он туда не один.


На пляже программист Алексей обнаружил, что осталось всего
08d9faefbe272bdf8fbb80773542e343.svg
(
3e07acf28d2303dfc70b16d918594082.svg
) свободных лежаков. Но среди всего этого множества лежаков программисту Алексею нужно было всего лишь 2: для него самого и для того (или той), с кем он пришел. Так как программист Алексей очень любил порядок, то он хотел, чтобы лежаки были как можно более похожи друг на друга. Похожесть лежаков можно вычислить следующим образом:



  • Каждому лежаку каким-то образом по его внешним признакам назначается некоторое число
    122b006916bcacd39b883735fa907bfa.svg
    (
    8e4cddedfead9717a26ada96e2b87c48.svg
    ,
    f946d809292e8e74cbb8f52b35fca3d0.svg
    ).
  • Затем непохожесть двух лежаков вычисляется как XOR (побитовое исключающее ИЛИ) чисел назначенных этим лежакам. Чем значение непохожести меньше, тем более похожи лежаки.


Помогите программисту Алексею понять, какого минимального значения непохожести лежаков он может достичь, сравнив попарно все свободные лежаки.



Входные данные



В первой строке задано число
175f98839ab732db76d5f20cd6ce2ce9.svg
(
9f2474bcb0bfe3fbf43e0e0fe84eb984.svg
) – количество тестов. Каждый тест состоит из двух строк.


В первой строке каждого теста задано число
08d9faefbe272bdf8fbb80773542e343.svg
(
3e07acf28d2303dfc70b16d918594082.svg
) – количество лежаков.


Во второй строке каждого теста заданы
08d9faefbe272bdf8fbb80773542e343.svg
чисел
122b006916bcacd39b883735fa907bfa.svg
(
8e4cddedfead9717a26ada96e2b87c48.svg
,
f946d809292e8e74cbb8f52b35fca3d0.svg
) – значения, которые были поставлены лежакам в соответствие по внешним признакам.


Сумма
08d9faefbe272bdf8fbb80773542e343.svg
по всем тестам не превосходит
8cb24ffc65f3fb246529ff336caaab93.svg
.



Выходные данные



Для каждого теста выведите по одной строке, в которой должно быть единственное число – минимальное значение непохожести.



Примеры



Входные данные

1
5
1 2 4 8 16

Выходные данные
3


Входные данные
2
2
2 4
4
2 4 6 8

Выходные данные
6
2




Примечание



В первом примере Алексей выберет лежаки со значениями 1 и 2.


В первой части второго примера Алексей может взять только лежаки со значениями 2 и 4. Во второй части он выберет лежаки со значениями 4 и 6.

Решение

Зададим все числа в их двоичном представлении, дополнив ведущими нулями так, чтобы их длины были одинаковы. Далее построим на этих значениях бор, где первое от корня ребро задается старшим разрядом: спускаясь вглубь дерева, вы переходите от старших разрядов к младшим.


Далее найдем все самые отдаленные от корня вершины, которые содержат ровно 2 числа. Каждой такой вершине поставим в соответствие число
4ec3e23638b6073b649999485c251c94.svg
, равное XOR между числами, которое содержит эта вершина. Тогда ответ на задачу – это минимум всех чисел
4ec3e23638b6073b649999485c251c94.svg
.


Покажем, что это и правда ответ на задачу. Зададим расстояние от всех выбранных вершин до ближайших листьев числом
16da507b2fc389688ef0659939dcc647.svg
– оно также указывает на самый старший разряд, в котором числа отличаются (если
4a0aa63ea24c30df62d0d98999c6486f.svg
, то числа равны). Тогда XOR между числами, принадлежащими разным сыновьям некоторой вершины, лежащей на расстоянии
817b92407f764f57af9226e50cc788fd.svg
от листьев, будет лежать в отрезке
3ed7ba4c557613b306d6de94edbac4df.svg
(для
5b40985e97972b4df8fab6b79bb2197d.svg
ответ
f9c3c8e488ead4696749012f5ece6d13.svg
, ибо числа полностью совпадают, для
0b29963b000c32b3e30f3a9b7dab1fac.svg
ответ
3e4c77a6e7c579a778fa84a18b6f4be0.svg
, ибо числа отличаются только в последнем разряде, для
de11363fe5cd6d7abcc8f21cc2315897.svg
ответ либо 2, либо 3 и т. д.). Если же ответ задачи достигается не на выбранных нами числах, то по построению получается, что они отличаются в каком-то разряде, старше
16da507b2fc389688ef0659939dcc647.svg
. В таком случае числа будут отличаются хотя бы на
62949cc2886126a1d03b7c9a0a24c515.svg
. Получаем противоречие, ибо существует попарный XOR меньше.


На самом деле, задачу можно решить без помощи бора, просто отсортировав все числа и взяв попарный XOR между соседними после сортировки числами. Ведь все числа, которые попадали в самые глубокие вершины, содержащие ровно 2 числа, являются также соседними числами после сортировки. Это действительно так, поскольку по построению бора ни одно другое число не может иметь значение из отрезка
58b7f7be8c846796f6af0f80072b3431.svg
, где
372e18546a3b7abb94c2672708bc5dfe.svg
и
302c7204ea9987e698a70307646abd71.svg
– числа из некоторой выбранной нами вершины.


Код на Python

import sys


def read_int():
return int(sys.stdin.readline())


def read_ints():
return list(map(int, sys.stdin.readline().split()))


def main():
t = read_int()
for _ in range(t):
n = read_int()
a = sorted(read_ints())
r = a[0] ^ a[1]
for i in range(1, n):
r = min(r, a[i-1] ^ a)

print(r)


if __name__ == '__main__':
main()

Код на C++

#include
#include
#include

using namespace std;

int main() {
int t;
scanf("%d", &t);
while (t--) {
int n;
scanf("%d", &n);
int* v = (int*) malloc(sizeof(int) * n);
for (int i = 0; i < n; ++i) {
scanf("%d", &v);
}
sort(v, v + n);
int ans = INT32_MAX;
for (int i = 1; i < n; ++i) {
ans = min(ans, v ^ v[i - 1]);
}
printf("%d\n", ans);
}
return 0;
}

Код на Java

import java.io_OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.StringTokenizer;
import java.util.Arrays;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.InputStream;
import java.lang.Math;
import java.util.Scanner;

public class Main {
public static void main(String[] args) {
InputStream inputStream = System.in;
OutputStream outputStream = System.out;
Scanner in = new Scanner(inputStream);
PrintWriter out = new PrintWriter(outputStream);
PairwiseXor solver = new PairwiseXor();
int n = in.nextInt();
for (int i = 1; i code>

D. Перемещение чанков


Ограничение по времени на тест

1 с

Ограничение по памяти на тест

256 МБ

Ввод

стандартный ввод

Вывод

стандартный вывод

Хранение данных в распределенном хранилище является крайне сложной задачей, однако инженеры Яндекса успешно с ней справляются.


Один из кластеров хранилища состоит из
e2e33f15a96008ca33579599483c4531.svg
серверов, на которых хранятся данные, разбитые на
08d9faefbe272bdf8fbb80773542e343.svg
чанков. Для дефрагментации кластера и возможности отключения старых серверов чанки периодически приходится перемещать между серверами. Перемещение чанков по одному неэффективно, поэтому они перемещают группами. Запрос на перемещение описывается четырьмя числами
83adcae30344fad5003d3a4a185bdbac.svg
и обозначает заказ на перемещение всех чанков с номерами от
30fb6814eea0091044df0e5de33dfbc2.svg
до
066939a33475dd671b845469b6526972.svg
включительно с сервера с номером
372e18546a3b7abb94c2672708bc5dfe.svg
на сервер с номером
302c7204ea9987e698a70307646abd71.svg
. После перемещения соотвествующие чанки с сервера с номером
372e18546a3b7abb94c2672708bc5dfe.svg
стираются. Таким образом, каждый чанк находится всегда ровно на одном сервере.


При построении распределенных систем очень важно избегать возможности конфликта изменений на кластере. Перед выполнением каждого из запросов необходимо убедиться, что все чанки, участвующие в запросе, находятся на ожидаемом сервере. Иными словами, перед выполнением запроса, описываемого параметрами
83adcae30344fad5003d3a4a185bdbac.svg
, необходимо убедиться, что все чанки с номерами от
30fb6814eea0091044df0e5de33dfbc2.svg
до
066939a33475dd671b845469b6526972.svg
включительно расположены на сервере с номером
372e18546a3b7abb94c2672708bc5dfe.svg
. В случае, если это неверно, запрос на перемещение чанков отменяется и система переходит к обработке следующего запроса. Если же это верно, то запрос осуществляется и все чанки от
30fb6814eea0091044df0e5de33dfbc2.svg
до
066939a33475dd671b845469b6526972.svg
включительно перемещаются с сервера
372e18546a3b7abb94c2672708bc5dfe.svg
на сервер
302c7204ea9987e698a70307646abd71.svg
.


По заданному начальному состоянию кластера и списку запросов перемещения чанков определите, какие запросы будут выполнены.



Входные данные



В первой строке заданы три целых числа
c3b8243605784cac510b0db7e98e8563.svg
и
d68cc4926bf74bae8fa3b51ca4a09ec8.svg
(
58b42fbae9394ddcd92665fcbb5a5dbe.svg
) — количество чанков на кластере, количество серверов, на которых располагаются чанки, и количество запросов, соответственно.


Во второй строке содержатся
08d9faefbe272bdf8fbb80773542e343.svg
чисел
08ba9c56140cf603308f9b84a26dc818.svg
(
e22329888b33a87a8e38bb869ac904f7.svg
),
bf83b532cd867d34004f8eded8c5c79a.svg
-е из которых обозначает номер сервера, где изначально располагается чанк с номером
bf83b532cd867d34004f8eded8c5c79a.svg
.


В следующих
d68cc4926bf74bae8fa3b51ca4a09ec8.svg
строках содержатся описания запросов перемещения чанков в хронологическом порядке.


Каждый запрос описывается четверкой чисел
f6032b58fe0a2ed1707e4e3a60cd0ef3.svg
(
cb1d390236126607211a530efc0b0f20.svg
) и обозначает заказ на перемещение чанков с номерами с
cdbd0f65bf0a1ff4171f268a95ec44c5.svg
по
300742edf17955ccd51dad394dde8966.svg
(включительно) с сервера
122b006916bcacd39b883735fa907bfa.svg
на сервер
efc54bd547b1a0f308a2ae4281e7d6ed.svg
.



Выходные данные



Выведите
d68cc4926bf74bae8fa3b51ca4a09ec8.svg
строк. В строке с номером
bf83b532cd867d34004f8eded8c5c79a.svg
выведите
3e4c77a6e7c579a778fa84a18b6f4be0.svg
, если запрос с номером
bf83b532cd867d34004f8eded8c5c79a.svg
будет выполнен, и
f9c3c8e488ead4696749012f5ece6d13.svg
, если нет.



Примеры



Входные данные

1 2 1
1
1 2 1 1

Выходные данные
1


Входные данные
1 2 1
1
2 1 1 1

Выходные данные
0


Входные данные
5 5 6
1 2 3 4 5
1 2 1 1
2 3 1 3
4 2 4 4
2 5 1 4
3 2 2 3
3 2 3 3

Выходные данные
1
0
1
0
0
1




Примечание



Рассмотрим третий пример. Изначально расположение чанков на серверах описывается массивом
07c4b27fe2e03880114beb0f122587ce.svg
.


В первом запросе требуется переместить чанк с номером
3e4c77a6e7c579a778fa84a18b6f4be0.svg
с сервера
3e4c77a6e7c579a778fa84a18b6f4be0.svg
на сервер
dfd8a0d2fe6ea30b4cde7f570eca349b.svg
. Чанк с номером
3e4c77a6e7c579a778fa84a18b6f4be0.svg
находится на сервере
3e4c77a6e7c579a778fa84a18b6f4be0.svg
, поэтому перемещение будет произведено. После выполнения запроса расположение чанков на серверах описывается массивом
598bfcda1cfac3e3d9610046de7ab1d3.svg
.


Во втором запросе требуется переместить чанки
db8e9d3f9d1d96507ff64a09084c5085.svg
и
3e328ec6538d48db0bbd8e0859e6d10a.svg
с сервера
dfd8a0d2fe6ea30b4cde7f570eca349b.svg
на сервер
3e328ec6538d48db0bbd8e0859e6d10a.svg
. Чанк
3e328ec6538d48db0bbd8e0859e6d10a.svg
расположен не на сервере
dfd8a0d2fe6ea30b4cde7f570eca349b.svg
, а на сервере
3e328ec6538d48db0bbd8e0859e6d10a.svg
, поэтому операция не будет выполнена. Расположение чанков на серверах останется прежним.


В третьем запросе требуется переместить чанк
578281d8997e0a562a02dd61422d408e.svg
с сервера
578281d8997e0a562a02dd61422d408e.svg
на сервер
dfd8a0d2fe6ea30b4cde7f570eca349b.svg
. Чанк
578281d8997e0a562a02dd61422d408e.svg
находится на сервере
578281d8997e0a562a02dd61422d408e.svg
, поэтому перемещение будет произведено. После выполнения запроса расположение чанков на серверах будет описываться массивом
e51c9c4da7e9602a2f1bb1c3218e1868.svg
.


В четвертом запросе требуется переместить чанки
91124cf2f32311e6e4dcf85d89f6f55a.svg
и
578281d8997e0a562a02dd61422d408e.svg
с сервера
dfd8a0d2fe6ea30b4cde7f570eca349b.svg
на сервер
4808200cb3668866f25a2e4437a6b74e.svg
. Так как чанк
3e328ec6538d48db0bbd8e0859e6d10a.svg
расположен на сервере
3e328ec6538d48db0bbd8e0859e6d10a.svg
, а не на сервере
dfd8a0d2fe6ea30b4cde7f570eca349b.svg
, запрос не будет выполнен и расположение чанков останется прежним.


В пятом запросе требуется переместить чанки
dfd8a0d2fe6ea30b4cde7f570eca349b.svg
и
3e328ec6538d48db0bbd8e0859e6d10a.svg
с сервера
3e328ec6538d48db0bbd8e0859e6d10a.svg
на сервер
dfd8a0d2fe6ea30b4cde7f570eca349b.svg
, но чанк
dfd8a0d2fe6ea30b4cde7f570eca349b.svg
расположен на сервере
dfd8a0d2fe6ea30b4cde7f570eca349b.svg
, поэтому запрос не будет выполнен и расположение чанков останется прежним.


В шестом запросе требуется переместить чанк
3e328ec6538d48db0bbd8e0859e6d10a.svg
с сервера
3e328ec6538d48db0bbd8e0859e6d10a.svg
на сервер
dfd8a0d2fe6ea30b4cde7f570eca349b.svg
. Чанк с номером
3e328ec6538d48db0bbd8e0859e6d10a.svg
находится на сервере
3e328ec6538d48db0bbd8e0859e6d10a.svg
, поэтому перемещение будет произведено и расположение чанков станет описываться массивом
95421be4c06df1bb415c3f04d64ef80a.svg
.

Решение и код
Можно хранить для каждого сервера по одному декартову дереву, содержащему все находящиеся на нем чанки. Для этого при поступлении запроса нужно достать из декартова дерева, соответствующего вершине
372e18546a3b7abb94c2672708bc5dfe.svg
, все чанки от
30fb6814eea0091044df0e5de33dfbc2.svg
до
066939a33475dd671b845469b6526972.svg
. Это можно сделать за 2 операции Split и одну операцию Merge, то есть за
21487538a6dbcc06fa5704effedb4282.svg
. Далее нужно проверить количество чанков, которые удалось достать из этого сервера. Если их в точности
c28ac52be0596e962b6b618e6aa15c3e.svg
, то валидация пройдена и нужно вывести
3e4c77a6e7c579a778fa84a18b6f4be0.svg
, если же нет, то ответ
f9c3c8e488ead4696749012f5ece6d13.svg
. Количество чанков можно проверить за
655b805d68b4b00a4e90f64eefbc6f1c.svg
.

Далее в зависимости от ответа вырезанные чанки нужно вернуть либо обратно на сервер
372e18546a3b7abb94c2672708bc5dfe.svg
, либо на сервер
302c7204ea9987e698a70307646abd71.svg
. Это делается с помощью одной операции Split и двух операций Merge, то есть за сложность
21487538a6dbcc06fa5704effedb4282.svg
. Итоговая сложность
0158825e490e9d922aa7f1233f2ab89b.svg
. Решение можно немного модифицировать и совершать меньше операций в случае ответа
f9c3c8e488ead4696749012f5ece6d13.svg
, но асимптотически сложность это не улучшит.



E. Разделение графа


Ограничение по времени на тест

1 с

Ограничение по памяти на тест

256 МБ

Ввод

стандартный ввод

Вывод

стандартный вывод

Дан взвешенный неориентированный граф
560bd97f235311a36dff00db005e6ab5.svg
, содержащий
08d9faefbe272bdf8fbb80773542e343.svg
вершин и
e2e33f15a96008ca33579599483c4531.svg
ребер.


Разбейте вершины графа на два не пустых множества так, чтобы ребро минимального веса, соединяющее вершины из одного множества, имело максимально большой вес.


Гарантируется, что граф не содержит петель и его нельзя разбить так, чтобы такого ребра не существовало.



Входные данные



В первой строке задано число вершин
08d9faefbe272bdf8fbb80773542e343.svg
(
0f2e5d68ff8a1961d6eec47b044610e4.svg
) и число ребер
e2e33f15a96008ca33579599483c4531.svg
(
2507954c64d8e1c64f8f34dc9c076d67.svg
) графа.


В следующих
e2e33f15a96008ca33579599483c4531.svg
строках заданы ребра в формате
784f98db37a499162c1479600e8465ed.svg
,
1c1778187263268cc347012d16da61e2.svg
,
1d0034a09108db7af7cf42ea23a91ecd.svg
(
5a39b930cc1a4653783ccaea20a0749d.svg
,
e4b1095aaf618e528bc003e1224057ea.svg
,
b8287ce12fc0cc3e4c466adeddf6c5b5.svg
), где
784f98db37a499162c1479600e8465ed.svg
и
1c1778187263268cc347012d16da61e2.svg
задают начальную и конечную вершину ребра, а
1d0034a09108db7af7cf42ea23a91ecd.svg
— его вес.



Выходные данные



В единственной строке выведите максимальный возможный вес ребра, которое имеет минимальный вес среди тех, что соединяют вершины, принадлежащие одному множеству.



Примеры



Входные данные

3 4
1 2 1
1 2 2
1 3 3
3 2 4

Выходные данные
4


Входные данные
4 5
1 2 1
2 3 1
3 4 1
4 1 1
2 4 2

Выходные данные
2




Примечание



Рассмотрим первый пример из условия. В нем возможно всего 3 различных разбиения, удовлетворяющих условию, рассмотрим каждое из них:


  • 72cef0e99a9c7ac968b44cbc2128d8a5.svg
    , в таком случае ребра
    3e4c77a6e7c579a778fa84a18b6f4be0.svg
    и
    dfd8a0d2fe6ea30b4cde7f570eca349b.svg
    будут будут соединять вершины из одного множества, то есть ответ
    3e4c77a6e7c579a778fa84a18b6f4be0.svg
    ;
  • b259fabc50724b5c30686509a4fc8eb4.svg
    , в таком случае только ребро
    3e328ec6538d48db0bbd8e0859e6d10a.svg
    будет соединять вершины из одного множества, то есть ответ
    3e328ec6538d48db0bbd8e0859e6d10a.svg
    ;
  • 23f317c03cf3edd8894f4434a188ddcc.svg
    , в таком случае только ребро
    578281d8997e0a562a02dd61422d408e.svg
    будет соединять вершины из одного множества, то есть ответ
    578281d8997e0a562a02dd61422d408e.svg
    .


Получаем, что ответ на тест достигается при третьем варианте разбиения.


Во втором тесте ответ достигается на разбиении
0ff493e7228743bd0202ca0b6e5c28ea.svg
. Легко убедиться, расписав все возможные разбиения, что не существует разбиения данного графа, на котором ответ был бы больше.

Решение

Решить задачу с помощью бинарного поиска по ответу.


Предположим, что ответ —
817b92407f764f57af9226e50cc788fd.svg
. Тогда все ребра, вес которых меньше либо равен
817b92407f764f57af9226e50cc788fd.svg
, должны соединять вершины из разных множеств, то есть двудольным является граф, построенный на ребрах с весом, меньшим либо равным
817b92407f764f57af9226e50cc788fd.svg
. Покажем монотонность результата построения в зависимости от
817b92407f764f57af9226e50cc788fd.svg
. Так, если для некоторого
817b92407f764f57af9226e50cc788fd.svg
можно построить граф, удовлетворяющий условию, то граф можно построить и для всех
5bc8dd68843cfe22ca2dbb2ecd151e70.svg
. Это следствие того, что для
9b34c4da5c757d4982bbd1b6f2e8998a.svg
можно взять такое же разбиение на множества, как и для
817b92407f764f57af9226e50cc788fd.svg
, а в таком случае ребра между вершинами из одного множества будут иметь вес, больше либо равный
f8d9ee6d05400fc8c5f5d1133a54f633.svg
. Если же для некоторого
817b92407f764f57af9226e50cc788fd.svg
нельзя построить граф, удовлетворяющий условию, то его нельзя построить и для
c5d23499afbb088b22ecbe70168d281b.svg
, так как граф, построенный на ребрах весом меньше
817b92407f764f57af9226e50cc788fd.svg
, не является двудольным (то есть существует ребро между вершинами из одного множества), а добавлением ребер нельзя восстановить двудольность графа.


Теперь опишем проверку того, что граф удовлетворяет условию, то есть является двудольным. Для этого нужно запустить обход всех вершин (например, в глубину либо в ширину) для каждой компоненты связности. Во время обхода каждой вершине нужно поставить в соответствие
f9c3c8e488ead4696749012f5ece6d13.svg
либо
3e4c77a6e7c579a778fa84a18b6f4be0.svg
. Это делается так:


  • Начинаем обход с некоторой вершины, в соответствие которой ставится
    f9c3c8e488ead4696749012f5ece6d13.svg
    .
  • При переходе по ребру от вершины
    372e18546a3b7abb94c2672708bc5dfe.svg
    к вершине
    302c7204ea9987e698a70307646abd71.svg
    вершине
    302c7204ea9987e698a70307646abd71.svg
    ставится в соответствие число, противоположное поставленному в соответствие вершине
    372e18546a3b7abb94c2672708bc5dfe.svg
    (например, если
    e7965dda46f482d032134743ea711648.svg
    , то
    e30ea7e1b769c35875162f055e4c7320.svg
    , и наоборот).

После обхода нужно проверить, что не существует ребра между вершинами, которым соответствует одно и то же число. Если это так, то заданный граф является двудольным.

Сложность одной итерации бинарного поиска равна
8fe7b7c623744e5a9e0847cbbb8937bf.svg
, а решение целиком имеет сложность
ce1396f3153c7754948c70b53eb80699.svg
.


Код на Python

import sys

def bfs(g, cl, m, v):
qu = [v]
cl[v] = 1

l = 0
while l != len(qu):
v = qu[l]
l += 1

for u, w in g[v]:
if w > m:
continue
if cl == 0:
cl = 3 - cl[v]
qu.append(u)
elif cl[v] == cl:
return False

return True

def check(g, m):
cl = [0 for i in range(len(g))]

for i in range(len(g)):
if cl == 0 and not bfs(g, cl, m, i):
return False

return True

def main():
n, m = map(int, sys.stdin.readline().split())
ed = [tuple(map(int, sys.stdin.readline().split())) for i in range(m)]

g = [[] for i in range(n)]
mx = 0
for v, u, w in ed:
g[v - 1].append((u - 1, w))
g[u - 1].append((v - 1, w))
mx = max(mx, w)

if check(g, mx):
sys.stdout.write(str(mx) + "\n")
return

l = 0
r = mx + 1

while r - l > 1:
m = (l + r) // 2
if check(g, m):
l = m
else:
r = m

sys.stdout.write(str(r) + "\n")

main()

Код на C++

#include
#include
#include
#include
#include

struct Solution {
int n, m;
std::vector>> graph;
std::vector colors;

bool dfs(int v, int color, int bound) {
colors[v] = color;
for (const auto &edge : graph[v]) {
if (edge.second >= bound) {
continue;
}
if (colors[edge.first] == color) {
return false;
}
if (colors[edge.first] == -1) {
if (!dfs(edge.first, 1 - color, bound)) {
return false;
}
}
}
return true;
}

bool check(int bound) {
for (int i = 0; i < n; i++) {
if (colors == -1) {
if (!dfs(i, 0, bound)) {
return false;
}
}
}
return true;
}

void run(std::istream &in, std::eek:stream &out) {
in >> n >> m;
graph.assign(n, std::vector>());
for (int i = 0; i < m; i++) {
int u, v, w;
in >> u >> v >> w;
u--;
v--;
graph.emplace_back(v, w);
graph[v].emplace_back(u, w);
}
int l = 0;
int r = 1000000001;
while (r - l > 1) {
int m = (l + r) / 2;
colors.assign(n, -1);
if (!check(m)) {
r = m;
} else {
l = m;
}
}
out code>

Код на Java

import java.io_OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io_OutputStream;
import java.io.PrintWriter;
import java.util.Arrays;
import java.io.BufferedWriter;
import java.util.InputMismatchException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.io.Writer;
import java.io_OutputStreamWriter;
import java.io.InputStream;

/**
* Built using CHelper plug-in
* Actual solution is at the top
*/
public class Graph_AD_Correct {
public static void main(String[] args) {
InputStream inputStream = System.in;
OutputStream outputStream = System.out;
InputReader in = new InputReader(inputStream);
OutputWriter out = new OutputWriter(outputStream);
Graph solver = new Graph();
solver.solve(1, in, out);
out.close();
}

static class Graph {
public void solve(int testNumber, InputReader in, OutputWriter out) {
int n = in.readInt();
int m = in.readInt();
//noinspection unchecked
List[] g = new List[n];
for (int i = 0; i < n; i++) {
g = new ArrayList<>();
}
int left = Integer.MAX_VALUE;
int right = Integer.MIN_VALUE;
for (int i = 0; i < m; i++) {
int x = in.readInt() - 1;
int y = in.readInt() - 1;
int w = in.readInt();
g[x].add(new Graph.Edge(y, w));
g[y].add(new Graph.Edge(x, w));
left = Math.min(left, w);
right = Math.max(right, w);
}
int[] color = new int[n];
int ans = -1;
while (left 2;
if (isBipartite(n, color, g, mid)) {
ans = mid;
left = mid + 1;
} else {
right = mid - 1;
}
}
if (ans == -1) {
throw new AssertionError();
}
out.printLine(ans);
}

private boolean isBipartite(int n, int[] color, List[] g, int mid) {
Arrays.fill(color, -1);
for (int i = 0; i < n; i++) {
if (color == -1) {
if (!dfs(i, -1, 0, color, g, mid)) {
return false;
}
}
}
return true;
}

private boolean dfs(int x, int p, int curColor, int[] color, List[] g, int mid) {
color[x] = curColor;
for (Graph.Edge e : g[x]) {
if (e.w >= mid) {
continue;
}
int y = e.to;
if (y == p) {
continue;
}
if (color[y] == color[x]) {
return false;
}
if (color[y] == -1 && !dfs(y, x, 1 - curColor, color, g, mid)) {
return false;
}
}
return true;
}

static class Edge {
int to;
int w;

Edge(int to, int w) {
this.to = to;
this.w = w;
}

}

}

static class OutputWriter {
private final PrintWriter writer;

public OutputWriter(OutputStream outputStream) {
writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(outputStream)));
}

public OutputWriter(Writer writer) {
this.writer = new PrintWriter(writer);
}

public void close() {
writer.close();
}

public void printLine(int i) {
writer.println(i);
}

}

static class InputReader {
private InputStream stream;
private byte[] buf = new byte[1024];
private int curChar;
private int numChars;
private InputReader.SpaceCharFilter filter;

public InputReader(InputStream stream) {
this.stream = stream;
}

public int read() {
if (numChars == -1) {
throw new InputMismatchException();
}
if (curChar >= numChars) {
curChar = 0;
try {
numChars = stream.read(buf);
} catch (IOException e) {
throw new InputMismatchException();
}
if (numChars '9') {
throw new InputMismatchException();
}
res *= 10;
res += c - '0';
c = read();
} while (!isSpaceChar(c));
return res * sgn;
}

public boolean isSpaceChar(int c) {
if (filter != null) {
return filter.isSpaceChar(c);
}
return isWhitespace(c);
}

public static boolean isWhitespace(int c) {
return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
}

public interface SpaceCharFilter {
public boolean isSpaceChar(int ch);

}

}
}

F. Поиск


Ограничение по времени на тест

2 с

Ограничение по памяти на тест

512 МБ

Ввод

стандартный ввод

Вывод

стандартный вывод

Разработчики Яндекс.Поиска постоянно работают над улучшением алгоритмов ранжирования. Один из алгоритмов, который хотят опробовать, таков.


Поисковый алгоритм состоит из трех этапов: поиск по базе, отбор документов и фильтрация. Во время поиска по базе отбираются
08d9faefbe272bdf8fbb80773542e343.svg
документов-кандидатов для показа пользователю. Каждому документу сопоставляется целое число — релевантность, показывающее, насколько документ подходит пользователю. Чем выше релевантность, тем лучше документ подходит по данному запросу. Обратите внимание, что релевантность может быть даже отрицательной в случае, если документ не имеет никакого отношения к запросу.


Ранжирующий алгоритм хранит данные в закольцованном списке, таким образом, после документа с номером
bf83b532cd867d34004f8eded8c5c79a.svg
(
48f8eb986179e96ba18449a97cf92914.svg
) следующим в списке является документ с номером
b2d78141c2233ba9627a56bf0e895287.svg
, а следующим после документа с номером
08d9faefbe272bdf8fbb80773542e343.svg
будет документ с номером
3e4c77a6e7c579a778fa84a18b6f4be0.svg
. Во время отбора документов алгоритм выберет некоторый документ (назовем его начальным) и сколько-то следующих за начальным документов, которые будут переданы на фильтрацию.


Во время фильтрации из списка документов, полученных при отборе, может быть удалено несколько документов, но не более
16da507b2fc389688ef0659939dcc647.svg
. Кроме того, после фильтрации должен остаться хотя бы один документ. Оставшиеся после фильтрации документы показываются пользователю.


Релевантностью выдачи назовем сумму релевантностей документов в ней. По релевантностям документов, полученных во время поиска, определите, какую наибольшую релевантность выдачи можно получить при оптимальной работе алгоритма.



Входные данные



В первой строке задано целое число
9b00124e411362185d05b841bc32695f.svg
— количество тестовых случаев. Далее следует описание тестовых случаев.


Описание каждого теста состоит из двух строк.


В первой строке задано два целых числа
08d9faefbe272bdf8fbb80773542e343.svg
и
16da507b2fc389688ef0659939dcc647.svg
(
a2d9c9397ad8e55e5b45eb061cb6cc04.svg
,
abf9c441055c46f6b3a3a36d45056cc8.svg
) — количество документов, полученных во время поиска, и максимальное количество документов, которые могут быть удалены после фильтрации соответственно.


Во второй строке содержатся
08d9faefbe272bdf8fbb80773542e343.svg
целых чисел
122b006916bcacd39b883735fa907bfa.svg
(
4e85b0be4b78bbb4483d9184c312ef65.svg
) — релевантности документов.


Гарантируется что сумма
08d9faefbe272bdf8fbb80773542e343.svg
по всем тестовым случаям не превосходит
a3461be79386363ec88a5d7c86f01446.svg
.



Выходные данные



Для каждого тестового случая выведите одно целое число — ответ на задачу.



Пример



Входные данные

6
4 0
1 -2 9 10
6 1
5 -5 5 -5 5 -5
6 2
5 -5 5 -5 5 -5
4 3
5 -5 5 -5
8 1
-3 -1 5 6 -200 -200 5 -1
3 1
-1 -2 -3

Выходные данные
20
10
15
10
14
-1




Примечание



В первом примере выгодно выбрать в качестве начального документ с релевантностью
87fb666e57e51cf3d068c23b82f29915.svg
и отправить на фильтрацию его и следующие 2 документа. На стадии фильтрации необходимо оставить все документы.


Во втором примере выгодно выбрать в качестве начального любой документ с релевантностью
4808200cb3668866f25a2e4437a6b74e.svg
, отправить на стадию фильтрации его и следующие за ним 2 документа. На стадии фильтрации необходимо удалить документ с релевантностью
43094448d117ab9ec8fd3c835032e6a4.svg
, таким образом, на выдачу попадут два документа с релевантностью
4808200cb3668866f25a2e4437a6b74e.svg
.


В пятом примере выгодно выбрать в качестве начального документ с номером
5e92dc5f04c55425625802c3067c2176.svg
и отправить на стадию фильтрации его и следующие
4808200cb3668866f25a2e4437a6b74e.svg
документов. Таким образом, на стадию фильтрации попадут документы с релевантностями
a36fe66122f1421402b8abe93a4095f1.svg
. На стадии фильтрации выгодно удалить документ с релевантностью
a706f36acf5c63e519154281c0055576.svg
, в результате чего на выдачу попадут документы с релевантностями
6eb77ae866af71713f1d73e2abc9dc13.svg
с суммарной релевантностью
7e64754f39d4bceb16dab3949096bcf6.svg
.


В последнем примере релевантность выдачи получится отрицательной, так как пользователю необходимо показать хотя бы один документ.

Решение

Если все релевантности документов отрицательны, то ответ — максимум по всем числам. Поскольку нам запрещено брать пустое множество, лучше обработать этот случай отдельно. Все последующие состояния динамики могут соответствовать пустому подотрезку с суммой
f9c3c8e488ead4696749012f5ece6d13.svg
, но это не улучшит ответ, поскольку мы всегда можем взять какой-то один неотрицательный элемент и это будет не хуже.


Решим задачу, если нет цикличности массива.


Пусть
d01909b96646300e052f062e2fe6aec6.svg
— максимальная сумма, которая может быть на каком-то подотрезке
6f67fb4b2a0f2c5a674ca19db8739927.svg
(
30fb6814eea0091044df0e5de33dfbc2.svg
тут не фиксировано и выбирается так, чтобы ответ был максимален), если из него удалили не более
16da507b2fc389688ef0659939dcc647.svg
элементов.


Пересчет:
d53d065e9e01cf1e9be467729380ef84.svg
, что соответствует:



  1. f9c3c8e488ead4696749012f5ece6d13.svg
    — попробовать начать новый «пустой» отрезок в этом месте.
  2. 4fd8a4e41eb3b06425c68f900254df56.svg
    — продолжить какой-то оптимальный отрезок, добавив в него i-й элемент.
  3. c102df636db691c854ab510a5185a624.svg
    — продолжить какой-то оптимальный отрезок, удалив из него i-й элемент.


Такая динамика считается за
e83b4c0f0af0f78e8e3fd01111d3d8ad.svg
, и ответом на задачу в этом случае является
ddb17326fc9913a7ab7045a04a567463.svg
.


Таким образом, мы учли все подотрезки массива, которые не проходят через стык первого и n-го элемента.


Теперь учтем оставшиеся отрезки.


Пусть
f33a53b89c82eaf6d7c4cf7162b3bcc6.svg
— максимальная сумма, которая может быть на некотором подотрезке
d468e8e107866a25fec673e22cc6f1a4.svg
, если из него удалили ровно
16da507b2fc389688ef0659939dcc647.svg
элементов.


Пересчет:
6442e061fdbd73d016fc411dc4af43e0.svg
, что соответствует:



  1. 68638163e70f54ebbe7dd70037d7f6f5.svg
    — продолжить отрезок, добавив в него i-й элемент.
  2. 2e5a46ac800c179e80a408af9d0b2b37.svg
    — продолжить отрезок, удалив из него i-й элемент.


В отличие от первой динамики, нет необходимости начинать новый отрезок, поскольку левая граница фиксирована.


Аналогично,
b7b1706c57be09888aa2756a2300729d.svg
— максимальная сумма, которая может быть на некотором подотрезке
bdde996825a73fdb765c241b884efa8a.svg
, если из него удалили ровно
16da507b2fc389688ef0659939dcc647.svg
элементов.


Пересчет:
bf0c5cccd51bcf4ffd386b9700b0fbd9.svg
, что соответствует:



  1. e1d848ff4b1198399f03fe5adbdcf2fa.svg
    — продолжить отрезок, добавив в него i-й элемент.
  2. 5bde742d4ada395dbc70b3533717829f.svg
    — продолжить отрезок, удалив из него i-й элемент.


Обе эти динамики можно посчитать за
e83b4c0f0af0f78e8e3fd01111d3d8ad.svg
.


Как теперь учесть все отрезки, проходящие через стык первого и n-го элемента? Переберем префикс
839f25c2746382debd4f08ea25ad5ecf.svg
, который войдет в ответ, а также количество элементов
d68cc4926bf74bae8fa3b51ca4a09ec8.svg
, которое мы удалили на этом префиксе. Самое оптимальное состояние — это
16641d342791dd29195256db021a8091.svg
. Теперь нужно соеденить это состояние с самым оптимальным суффиксом — если точнее, с
804cbc96ec7712e7f46f4e12061ef94b.svg
.


Этот максимум можно предпросчитать еще одной динамикой
6abe7c167e988a349e99942b88d40e48.svg
. Сложность такого предпросчета —
e83b4c0f0af0f78e8e3fd01111d3d8ad.svg
, и после него можно находить оптимальный суффикс для каждой фиксированной пары
839f25c2746382debd4f08ea25ad5ecf.svg
и
d68cc4926bf74bae8fa3b51ca4a09ec8.svg
за
655b805d68b4b00a4e90f64eefbc6f1c.svg
.


Получаем решение за
e83b4c0f0af0f78e8e3fd01111d3d8ad.svg
времени и памяти.


Код на Python

import sys

def solve():
n, k = map(int, sys.stdin.readline().split())
arr = list(map(int, sys.stdin.readline().split()))

ans = max(arr)
if ans code>

Код на C++

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#pragma comment(linker, "/STACK:256000000")

using namespace std;

typedef long long int ll;
typedef long double ld;

const int INF = 1000 * 1000 * 1000 + 21;
const ll LLINF = (1ll = 1; --i) {
for (int j = 0; j = 1; --i) {
for (int j = 0; j /freopen("output.txt", "w", stdout);
#else
//freopen("", "r", stdin);
//freopen("", "w", stdout);
#endif

int t;
scanf("%d", &t);
while (t--) {
solve();
}

return 0;
}

Код на Java

import java.io_OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io_OutputStream;
import java.io.PrintWriter;
import java.io.BufferedWriter;
import java.io.Writer;
import java.io_OutputStreamWriter;
import java.io.IOException;
import java.io.InputStream;

/**
* Built using CHelper plug-in
* Actual solution is at the top
*
* @author ch_egor
*/
public class Main {
public static void main(String[] args) {
InputStream inputStream = System.in;
OutputStream outputStream = System.out;
InputReader in = new InputReader(inputStream);
OutputWriter out = new OutputWriter(outputStream);
MaxSum solver = new MaxSum();
int testCount = Integer.parseInt(in.next());
for (int i = 1; i = 1; --i) {
for (int j = 0; j = 1; --i) {
for (int j = 0; j = numChars) {
curChar = 0;
try {
numChars = stream.read(buf);
} catch (IOException e) {
throw new UnknownError();
}
if (numChars '9') {
throw new UnknownError();
}
res *= 10;
res += c - '0';
c = read();
} while (!isSpaceChar(c));
return res * sgn;
}

public String nextString() {
int c = read();
while (isSpaceChar(c)) {
c = read();
}
StringBuilder res = new StringBuilder();
do {
if (Character.isValidCodePoint(c)) {
res.appendCodePoint(c);
}
c = read();
} while (!isSpaceChar(c));
return res.toString();
}

public boolean isSpaceChar(int c) {
if (filter != null) {
return filter.isSpaceChar(c);
}
return isWhitespace(c);
}

public static boolean isWhitespace(int c) {
return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
}

public String next() {
return nextString();
}

public interface SpaceCharFilter {
public boolean isSpaceChar(int ch);

}

}
}
 
Сверху Снизу