Разбираем реальные задачи для кандидатов в Яндекс
Хабр, это снова я, Алексей Рак (фото не мое). В прошлом году, помимо основной работы, мне довелось стать одним из автором задач для кандидатов в Яндекс. Сегодня наша команда впервые за долгое время публикует на Хабре реальные задачи для разработчиков, которые устраиваются в компанию. Эти задачи использовались до февраля 2020 года при отборе на стажировку для бэкендеров. Решения проверял компьютер. Сейчас кандидатам достаются похожие задания.
Разборы и код сознательно спрятаны в спойлеры. Если вы интересуетесь стажировкой или готовитесь к собеседованиям в большие IT-компании, попробуйте решить одну или несколько задач, прежде чем смотреть разбор. Код представлен на Python, C++ и Java.
Авторы и мои коллеги: задача A — Павел Дорошкевич, B и F — Егор Чунаев, E — Андрей Халявин, C и D — я.
A. День рождения Васи
Решение и код
B. Закрытый ключ
Решение и код
C. Программист на пляже
Решение и код
D. Перемещение чанков
Решение и код
E. Разделение графа
Решение и код
F. Поиск
Решение и код
A. День рождения Васи
Вася и его друзья очень любят вкусно поесть. На свой день рождения каждый обязан удивить других, приготовив новые вкусные и полезные блюда.
Сегодня у Васи день рождения, и он позвал своих друзей отведать n своих самых лучших блюд. Название каждого из них состоит из строчных букв английского алфавита, цифр и знака подчеркивания.
Для приготовления блюда с номером i требуется zi ингредиентов. Для каждого ингредиента известно его название, требуемое количество для одной порции блюда, а также единица измерения, в которой это количество задано. Помимо этого, Васе известно, что i-й кулинарный шедевр захотят попробовать ci друзей.
Используются следующие единицы измерения:
- g, kg — граммы и килограммы соответственно;
- l, ml — литры и миллилитры соответственно;
- cnt, tens — одна штука и десять штук соответственно.
В одном килограмме 1000 грамм и в одном литре 1000 миллилитров. Делать перевод из одной единицы измерения в другую можно тогда и только тогда, когда они одновременно обозначают или массу, или объем, или количество.
У Васи есть два справочника ингредиентов. В первом для каждого ингредиента указано его количество в упаковке и цена за упаковку. Во втором справочнике для каждого ингредиента указано содержание белков, жиров, углеводов и энергетическая ценность некоторого количества данного ингредиента.
Васе нужно приготовить все блюда, при этом не купить ничего лишнего. Для этого ему нужно определить, какие ингредиенты и в каком количестве необходимо приобрести в магазине.
Так как друзья именинника очень следят за питанием, то они, перед тем как попробовать Васины блюда, захотят узнать всё: как Вася готовил блюда, их энергетическую ценность и содержание белков, жиров и углеводов. Васе нужно подготовить эту информацию.
Необходимо помочь имениннику подсчитать, сколько требуется потратить денег на покупку продуктов в магазине, какие ингредиенты и в каком количестве нужно купить, а также для каждого блюда посчитать содержание белков, жиров, углеводов и энергетическую ценность, если его съесть полностью.
Входные данные
Первая строка содержит целое число n (1 ⩽ n ⩽ 1000) — количество блюд, которое решил приготовить Вася.
Затем следует описание n блюд. В первой строке содержатся строка di и целые числа сi, zi (1 ⩽ сi, zi ⩽ 100) — название блюда, количество друзей, желающих отведать данное блюдо, и количество ингредиентов необходимых для приготовления. Название блюда состоит из строчных букв английского алфавита, цифр и знака подчеркивания. Его длина не превосходит 20 символов.
В следующих zi строках содержатся описания ингредиентов. В строке с номером j содержатся строка si, j — название ингредиента, целое число ai, j (1 ⩽ ai, j ⩽ 1000) — требуемое количество ингредиента и строка ui, j — название единицы измерения количества. Название ингредиента состоит из строчных букв английского алфавита, цифр и знака подчеркивания. Длина строки не превосходит 20 символов.
Следующая строка содержит целое число k (1 ⩽ k ⩽ 1000) — количество ингредиентов в каталоге цен.
В каждой из следующих k строк содержатся четыре значения ti pi ai ui, описывающих ингредиент.
- ti — название ингредиента, состоящее из строчных букв английского алфавита, цифр и знака подчеркивания. Длина строки не превосходит 20 символов;
- pi — стоимость ингредиента, заданная целым числом (1 ⩽ pi ⩽ 1000);
- ai — количество ингредиента в упаковке в единицах, заданное целым числом (1 ⩽ ai ⩽ 1000);
- ui — единица измерения количества (l, ml, g, kg, cnt или tens).
Следующая строка содержит число m (1 ⩽ m ⩽ 1000) — количество ингредиентов в каталоге еды.
Далее расположены m строк, в каждой из которых содержатся семь значений ti ai ui pri fi chi fvi, описывающих ингредиент.
- ti — название ингредиента, состоящее из строчных букв английского алфавита, цифр и знака подчеркивания. Длина строки не превосходит 20 символов;
- ai — количество ингредиента, для которого указаны характеристики ингредиента, заданное целым числом (1 ⩽ ai ⩽ 1000);
- ui — единица измерения (l, ml, g, kg, cnt или tens);
- pri fi chi fvi — содержание белков, жиров, углеводов и энергетическая ценность ингредиента соответственно, заданные вещественными числами с не более чем шестью знаками после запятой (0 ⩽ pri, fi, chi ⩽ 1000, 0 ⩽ fvi ⩽ 10 000).
Гарантируется, что:
- в каталогах перечислены все ингредиенты, необходимые для приготовления блюд;
- не существует двух блюд с одинаковым названием;
- не существует двух ингредиентов в одном каталоге с одинаковым названием;
- не существует двух ингредиентов в одном блюде с одинаковым названием;
- для любых двух упоминаний ингредиента единицы измерения, в которых заданы его количества, можно перевести друг в друга.
Выходные данные
Первая строка должна содержать одно целое число: сумма, которую нужно потратить Васе на подготовку к празднику.
Далее должны следовать k строк, в каждой из которых через пробел указано название ингредиента и целое число — количество упаковок, которое необходимо купить. Ингредиенты, выведенные в этих k строках, должны соответствовать ингредиентам, описанным в первом справочнике.
В следующих n строках через пробел должны быть указаны название блюда и его характеристики, описанные четырьмя вещественными числами: белки, жиры, углеводы и энергетическая ценность.
Ингредиенты и блюда разрешается выводить в любом порядке.
Ваш ответ будет считаться правильным, если все целые числа совпадают с соответствующими числами в ответе жюри, а для всех вещественных чисел в ответе их абсолютная или относительная погрешность не превосходит 10–3. Более формально, пусть вещественное число в вашем ответе равно A, а соответствующее число в ответе жюри равно B. Тогда число A будет считаться корректным, если .
Пример
Примечание
В первом примере Васе необходимо приготовить 7 бутербродов и 9 омлетов.
Для приготовления всех первых блюд необходимо 10 ⋅ 7 грамм масла, 2 1 7 кусочков хлеба и 30 ⋅ 7 грамм колбасы. В каждом из бутерброде будет содержаться грамм белков,
грамм жиров и
грамм углеводов. Энергетическаая ценность составит
.
Для приготовления всех вторых блюд необходимо 4 ⋅ 9 = 36 яиц, 120 ⋅ 9 = 1080 миллилитров молока, 9 грамм соли, 50 ⋅ 9 = 450 грамм колбасы. В каждом омлете будет содержаться грамм белков,
и
углеводов. Энергетическая ценность составит
ккал.
Всего необходимо 70 грамм масла, 36 яиц, 1080 литров молока, 9 грамм соли, 210 + 450 = 660 грамм колбасы и 14 кусочков тостового хлеба.
Таким образом, в магазине нужно купить одну упаковку масла, 4 десятка яиц, 2 упаковки колбасы и молока, по одной упаковке соли и тостового хлеба, заплатив 120 + 61 ⋅ 4 + 100 ⋅ 2 + 58 ⋅ 2 + 14 + 40 = 734 рубля.
Для решения задачи нужно:
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 достаточно для выполнения операций и вывода результатов. Его можно использовать, если корректно делать округление.
Асимптотическая сложность алгоритма при использовании хэш-таблиц для каталога и справочника: , где zi — количество ингредиентов в i-м блюде.
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()#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include 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[i] = new Recipes.Recipe();
recipes[i].name = in.nextString();
recipes[i].mult = in.nextLong();
int ingredientsSize = in.nextInt();
recipes[i].ingredients = new Recipes.Ingredient[ingredientsSize];
for (int j = 0; j < recipes[i].ingredients.length; ++j) {
recipes[i].ingredients[j] = new Recipes.Ingredient();
recipes[i].ingredients[j].name = in.nextString();
recipes[i].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[i].ingredients.length; ++j) {
Recipes.IngredientBuyInfo curBuyInfo = ingredientBuyInfo.get(recipes[i].ingredients[j].name);
curBuyInfo.buyCnt += recipes[i].mult * recipes[i].ingredients[j].cnt;
Recipes.IngredientEnergyInfo curEnergyInfo = ingredientEnergyInfo.get(recipes[i].ingredients[j].name);
for (int p = 0; p < CNT_ENERGY; ++p) {
recipes[i].energyArr[p] += ((double) recipes[i].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[i].name + " ");
for (int j = 0; j < CNT_ENERGY; ++j) {
out.print(String.format("%.20f", recipes[i].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[i] = 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[i] = 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 <= 0) {
return -1;
}
}
return buf[curChar++];
}
public int nextInt() {
int c = read();
while (isSpaceChar(c)) {
c = read();
}
int sgn = 1;
if (c == '-') {
sgn = -1;
c = read();
}
int 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 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[i]);
}
}
public void println(Object... objects) {
print(objects);
writer.println();
}
public void close() {
writer.close();
}
public void println(long i) {
writer.println(i);
}
}
} B. Закрытый ключ
Во всех крупных IT-компаниях немалое внимание уделяется вопросам информационной безопасности, и Яндекс — не исключение.
Дима и Егор разрабатывают новый сервис YD (Yandex Dorogi) и в данный момент проводят аудит его безопасности. Для шифрования пользовательских данных в YD используется алгоритм шифрования с открытым ключом YS (Yandex Shifrovatel).
Схема работы YS такова: для каждого сервиса генерируется закрытый ключ (), где
и
— натуральные числа. По закрытому ключу (
) генерируется открытый ключ (НОД (
), НОК (
)), который доступен всем пользователям. Если злоумышленник сможет по открытому ключу получить закрытый ключ, то он получит доступ ко всем данным YD и принесет сервису непоправимый вред. Конечно же, Егор и Дима не могут этого допустить, поэтому они хотят сделать так, чтобы злоумышленнику пришлось перебрать очень много вариантов открытого ключа, прежде чем он сможет его угадать.
Дима уже сгенерировал закрытый ключ для YD и получил на его основе открытый ключ (). Егору сразу же стало интересно, сколько вариантов закрытого ключа придется перебрать злоумышленнику для взлома YD в худшем случае, иными словами, сколько существует закрытых ключей (
) таких, что открытым ключом для них является (
). К сожалению, у Егора есть много других задач, очень важных для запуска YD, поэтому он просит вас вычислить это количество за него.
Входные данные
В первой строке содержатся два целых числа и
(
) — описание открытого ключа.
Выходные данные
Выведите одно целое число — количество закрытых ключей, для которых данный ключ является открытым.
Примеры
Входные данные
5 10
Выходные данные
2
Входные данные
10 11
Выходные данные
0
Входные данные
527 9486
Выходные данные
4
Примечание
В первом примере существует два закрытых ключа, для которых () является открытым ключом: (
) и (
).
Во втором примере Дима ошибся, потому что ни один закрытый ключ не порождает открытый ключ ().
В третьем примере подходящими закрытыми ключами являются (), (
), (
), (
).
НОД (наибольшим общим делителем) двух натуральных чисел и
называется наибольшее число
такое, что
делится на
и
делится на
. Например, НОД (
) равен
, а НОД (
) равен
.
НОК (наименьшим общим кратным) двух натуральных чисел и
называется наименьшее число
такое, что
делится на
и
делится на
. Например, НОК (
) равен
, а НОК (
) равен 20.
Если не делится на
, то ответ, очевидно, равен нулю.
Иначе рассмотрим какую-то пару ,
, что
, а
. С каждой такой парой можно сопоставить два числа
и
такие, что
, а
.
Иными словами, задачу можно свести к задаче нахождения количества пар взаимно простых чисел ,
таких, что их
равен
.
Далее будем предполагать что , если это не так, то сводим исходную задачу к задаче
,
.
Первый вариант решения
Решение за .
Зафиксируем , тогда ему в пару может подойти только
, поскольку
. Единственное, что надо проверить, это равенство
, это можно сделать алгоритмом Евклида за
.
Под факторизацией подразумевается любой способ нахождения делителей. С учетом достаточно маленьких ограничений их можно найти просто проверкой всех целых чисел от до
, что имеет сложность
. Известно, что количество делителей точно не превосходит
, так что проверку, что
, нужно выполнить не более
раз, что имеет сложность
.
Получаем решение за .
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include Второй вариант решения
Решение за .
Рассмотрим простое число , на которое делится
. Если
, то либо
, либо
должно делится на
, где
— максимальная степень, с которой
входит в
. Чтобы выполнялось
, одно из этих чисел не должно делиться на
.
Иными словами, для каждого простого числа, на которое делится , мы выбираем, пойдет оно в
или в
. Тогда ответ на задачу равен
.
x, y = map(int, input().split())
if y % x != 0:
print(0)
else:
y //= x
i = 2
ans = 0
while i * i <= y:
if y % i == 0:
ans += 1
while y % i == 0:
y //= i
i += 1
if y != 1:
ans += 1
print(2 ** ans)#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include 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 <= y; ++i) {
if (y % i == 0) {
++ans;
while (y % i == 0) {
y /= i;
}
}
}
if (y != 1) {
++ans;
}
out.println(1L << ans);
}
}
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 =
