Educational Round #1 — Разборы
Пусть — длины сторон треугольника. Треугольник является прямоугольным, если выполняется одно из следующих равенств:
(гипотенузой является сторона )
(гипотенузой является сторона )
(гипотенузой является сторона )
Читаем входные данные до конца файла.
while(scanf("%d %d %d",&a,&b,&c)) {
Если встретились три нуля, то завершаем работу программы.
if (a + b + c == 0) break;
Проверяем, является ли треугольник прямоугольным. В зависимости от результата выводим ответ.
if ((a * a + b * b == c * c) || (a * a + c * c == b * b) || (b * b + c * c == a * a)) puts("right"); else puts("wrong"); }
В Хогвартсе проходит традиционная ежегодная олимпиада по теории магии среди младшекурсников. Завхозу школы, Аргусу Филчу, поручено распределить студентов по аудиториям.
Каждый факультет выставил на олимпиаду своих лучших учеников: от Гриффиндора участвуют студентов, от Слизерина — студентов, Пуффендуй представляет студентов, а Когтевран — студентов. В распоряжении Филча имеется аудиторий.
На аудитории наложено особое заклятие расширения, благодаря которому каждая аудитория может вместить любое количество студентов. Однако необходимо учитывать, что ученики одного факультета, оказавшиеся в одной аудитории, могут воспользоваться возможностью для жульничества, обмениваясь идеями по решению задач. Поэтому количество студентов одного факультета в одной аудитории должно быть минимально возможным.
Назовём рассадку оптимальной, если она минимизирует максимальное количество студентов одного факультета в любой аудитории.
Определите, какое минимальное количество студентов одного факультета Филчу всё же придётся посадить в одну аудиторию даже при оптимальной рассадке.
Вход. Первая строка содержит четыре целых числа и — количество учеников, представляющих факультеты Гриффиндор, Слизерин, Пуффендуй и Когтевран соответственно.
Во второй строке задано одно целое число — количество доступных аудиторий.
Выход. Выведите одно целое число — минимальное возможное количество студентов одного факультета в одной аудитории при оптимальной рассадке.
4 3 4 4 2
2
15 14 13 14 5
3
На самом крупном факультете учится студентов. Если попытаться равномерно распределить их по аудиториям, то в какой-то из них окажется не менее студентов. Именно такое минимальное количество студентов одного факультета придётся посадить в одну аудиторию даже при оптимальной рассадке.
Читаем входные данные.
scanf("%d %d %d %d", &g, &s, &h, &r); scanf("%d", &m);
Вычисляем .
mx = g; if (s > mx) mx = s; if (h > mx) mx = h; if (r > mx) mx = r;
Определим максимальное количество студентов, которое может оказаться в одной аудитории: .
res = mx / m; if (mx % m > 0) res++;
Выводим ответ.
printf("%d\n", res);
Четырехзначное число называется числом Армстронга, если сумма четвертых степеней его цифр равна самому числу.
Например: , следовательно является числом Армстронга.
Выведите все числа Армстронга от до .
Вход. Два целых числа и .
Выход. Выведите в одной строке все числа Армстронга в диапазоне от до .
1000 3000
1634
Переберем числа от до . Для каждого числа выделим цифры тысяч , сотен , десятков и единиц . Если число является числом Армстронга , то выводим его.
Читаем входные данные.
scanf("%d %d", &a, &b);
Перебираем числа от до .
for (i = a; i <= b; i++) {
Выделяем цифры числа .
x = a / 1000; y = i / 100 % 10; z = i / 10 % 10; u = i % 10;
Если число является числом Армстронга , то выводим его.
if (x * x * x * x + y * y * y * y + z * z * z * z + u * u * u * u == i) printf("%d ", i); } printf("\n");
Разделите числа на две группы так, чтобы абсолютная разность между суммами элементов этих групп была наименьшей из возможных.
Вход. Одно целое число .
Выход. В первой строке выведите два целых числа — количество элементов в первой и второй группах.
Во второй строке выведите элементы первой группы, а в третьей — элементы второй группы.
Элементы внутри каждой группы могут быть выведены в любом порядке. Каждое число от до должно быть включено ровно в одну из групп.
4
2 2 1 4 2 3
5
3 2 1 2 4 3 5
Заметим, что если числа и поместить в одну группу, а и поместить в другую (сумма чисел в каждой группе будет одинаковой), то задача сведётся к аналогичной задаче меньшей размерности, а именно к разбиению на группы чисел .
Для числа распределим по группам следующим образом:
Если остаются три числа , то их можно разделить на две группы: и .
Если остаются два числа , то разделим их на группы: и . Разность сумм чисел в группах составит .
Если остается одно число , то его можно поместить в любую группу. Разность сумм чисел в группах составит .
Если сумма всех чисел от до четная, то их можно разделить на две группы с одинаковой суммой.
Если сумма всех чисел от до нечетная, то их можно разделить на две группы с разницей сумм, равной 1.
Пример
Пусть . Числа в группы размещаем следующим образом:
Пусть . Числа в группы размещаем следующим образом:
Читаем входное значение .
scanf("%d", &n);
Устанавливаем начальные суммы чисел в группах и равными .
s1 = s2 = 0;
Перебираем числа от до по убыванию.
for (i = n; i > 0; i--)
Если , то заносим число в первую группу. Иначе заносим число во вторую группу.
if (s1 < s2) { s1 += i; a.push_back(i); } else { s2 += i; b.push_back(i); }
Выводим размеры групп.
printf("%d %d\n", a.size(), b.size());
Выводим числа первой группы.
for (i = 0; i < a.size(); i++) printf("%d ", a[i]); printf("\n");
Выводим числа второй группы.
for (i = 0; i < b.size(); i++) printf("%d ", b[i]); printf("\n");
Определим бесконечную последовательность следующим образом:
По заданным значениям вычислите .
Вход. Пять целых чисел .
Выход. Выведите значение .
12 2 3 1 0
8
10000000 2 3 10000000 10000000
2
Поскольку , хранение всех значений невозможно ни с использованием массива, ни с помощью структуры map из-за ограничений по памяти. Поэтому для вычислений будем использовать рекурсию, заданную в рекуррентном соотношении.
При этом значения для которых , будем сохранять в массиве , чтобы избежать повторных вычислений и ускорить выполнение программы.
Для хранения значений объявим массив .
#define MAX 5000000 long long m[MAX];
Функция f вычисляет значение .
long long f(long long n, long long p, long long q, long long x, long long y) {
Если , то .
if (n <= 0) return 1;
Если и значение уже вычислено (отлично от нуля), то возвращаем его.
if ((n < MAX) && m[n]) return m[n];
Выполняем рекурсивное вычисление значения в соответствии с формулой, приведённой в условии задачи.
long long temp = f(n/p-x,p,q,x,y) + f(n/q-y,p,q,x,y);
Если , сохраняем результат в массиве , чтобы избежать повторных вычислений в будущем.
if (n < MAX) m[n] = temp; return temp; }
Основная часть программы. Читаем входные данные.
scanf("%lld %lld %lld %lld %lld",&n,&p,&q,&x,&y);
Вычисляем и выводим ответ.
res = f(n,p,q,x,y); printf("%lld\n",res);
По заданным двум целым числам и найдите такое действительное число , что значение является максимальным. Известно, что
Можно доказать, что максимальное значение функции рационально. Выведите его в виде несократимой дроби.
Вход. Содержит не более тестов. Каждый тест состоит из одной строки, содержащей два целых числа и .
Выход. Для каждого теста выведите максимальное значение в виде несократимой дроби , где .
1 1 1 2
1/2 1/4
Рассмотрим, какие значения принимает выражение , где при фиксированных и . Например, .
Рассмотрим, например, функцию . Тогда
Согласно расширенному алгоритму Евклида, всегда существуют такие целые и , что . Следовательно, наименьшим положительным значением, которое может принимать выражение , является . Отсюда следует, что это выражение может принимать все возможные значения вида , где .
Теорема. Если , то эта функция принимает значения вида , где .
Доказательство. Действительно,
Поскольку числа и являются взаимно простыми, то всегда существуют такие и , что числитель дроби равен . Следовательно, функция может принимать все возможные значения вида , где .
Теперь рассмотрим исходную функцию
Ее значения — расстояния от точки до ближайшей точки вида . Для максимизации функции значение следует выбрать таким образом, чтобы оно находилось точно посередине между соседними точками и , где — целое число. Значение функции в такой точке равно , что и будет ответом.
Пример
Пусть . Очевидно, что . Равенство справедливо, так как существуют такие и , что (например, ). Из этого также следует, что , где .
Однако , и это будет наибольшим значением функции, так как точка находится на максимальном расстоянии от нулей функции.
Функция gcd вычисляет наибольший общий делитель чисел и .
long long gcd(long long a, long long b) { return b == 0 ? a : gcd(b, a % b); }
Функция lcm вычисляет наименьшее общее кратное чисел и .
long long lcm(long long a, long long b) { return a / gcd(a, b) * b; }
Основная часть программы. Читаем входные данные.
while (scanf("%lld %lld", &n, &m) == 2) {
Вычисляем и выводим ответ.
d = 2 * lcm(n, m); printf("%lld/%lld\n", 1, d); }
Задан массив из целых чисел. Сколькими способами можно выбрать подмножество его элементов так, чтобы их сумма была равна ?
Вход. В первой строке заданы два целых числа: — размер массива и — требуемая сумма.
Во второй строке заданы целых чисел: — элементы массива.
Выход. Выведите одно число — количество способов выбрать подмножество элементов массива, сумма которых равна .
4 5 1 2 3 2
3
6 7 1 3 2 2 1 4
8
Разобьем входной массив на две равные части: и . Первую часть сохраним в массив , вторую — в массив .
Сначала решим задачу для последовательности . Переберем все ее возможные подмножества (их будет не более ). Для каждого подмножества вычислим сумму его элементов , и для каждой такой суммы увеличим значение на . В качестве структуры данных можно использовать, например, unordered_map. Таким образом, будет содержать количество способов выбрать подмножество из массива с суммой элементов, равной .
Затем переберём все подмножества массива . Пусть — сумма элементов одного из таких подмножеств. Если объединить это подмножество со всеми подмножествами массива , сумма элементов которых равна , то в результате получится подмножество исходного массива, сумма элементов которого равна .
Пример
Рассмотрим второй пример. Разобьем входную последовательность на две части:
,
Переберем все подмножества множества :
; | ; |
; | ; |
; | ; |
; | ; |
Структура после обработки будет содержать следующие значения:
Теперь переберем все подмножества множества . В данном примере искомая сумма равна . Для каждой суммы , соответствующей подмножеству , будем прибавлять к ответу значение .
;
;
;
;
;
;
;
;
Искомая сумма равна , что и является ответом.
Объявим рабочие структуры данных.
vector<int> a, b; unordered_map<long long, long long> m;
Читаем входные данные. Первую половину последовательности заносим в массив , вторую — в массив .
scanf("%d %d", &n, &s); a.resize(n / 2); for (i = 0; i < n / 2; i++) scanf("%d", &a[i]); b.resize(n - n / 2); for (i = n / 2; i < n; i++) scanf("%d", &b[i - n / 2]);
Перебираем все подмножества последовательности и вычисляем суммы их элементов. В структуре unordered_map сохраняем, сколько раз каждая сумма встречается среди этих подмножеств. Если — сумма текущего подмножества, то значение увеличиваем на .
for (i = 0; i < (1 << a.size()); i++) { sum = 0;
Переменная содержит битовую маску текущего подмножества . Сумма его элементов вычисляется в переменной .
for (j = 0; j < a.size(); j++) if (i & (1 << j)) sum += a[j];
Для каждой найденной суммы sum увеличиваем значение на .
m[sum]++; }
Количество способов получить сумму из элементов исходного массива подсчитываем в переменной .
res = 0;
Перебираем все подмножества последовательности .
for (i = 0; i < (1 << b.size()); i++) { sum = 0;
Переменная содержит битовую маску текущего подмножества . Сумма элементов этого подмножества вычисляется в переменной .
for (j = 0; j < b.size(); j++) if (i & (1 << j)) sum += b[j];
Если объединить текущее подмножество множества , сумма элементов которого равна , со всеми подмножествами множества , сумма которых равна , то в результате получится подмножество исходного массива, сумма элементов которого равна .
if (m.count(s - sum) > 0) res += m[s - sum]; }
Выводим ответ.
printf("%lld\n", res);
Учёные генетики с планеты Олимпия снова проводят эксперименты с ДНК примитивных организмов. Геном организма представляет собой последовательность генов, каждый из которых можно закодировать с помощью одного натурального числа. Гены, которые кодируются одинаковыми числами, считаются одинаковыми, а гены, кодируемые различными числами, — разными.
Учёные уже создали примитивный организм и хотят модифицировать его геном таким образом, чтобы получить идеальный организм. Они считают, что в будущем это поможет разработать лекарства от множества болезней. Организм считается идеальным, если любые два одинаковых гена либо находятся на соседних позициях в геноме, либо между ними есть хотя бы один такой же ген.
За одну операцию учёные могут выбрать один или несколько одинаковых генов в геноме, удалить их, а затем вернуть их обратно в геном, возможно, на другие позиции. Поскольку каждая такая операция ослабляет организм, учёные стремятся минимизировать их количество при достижении цели.
Напишите программу, которая по заданному представлению генома определит наименьшее количество операций, необходимых для получения идеального организма.
Вход. Первая строка содержит количество генов в геноме примитивного организма. Во второй строке записано натуральных чисел, каждое из которых не превышает — последовательность генов в геноме.
Выход. Выведите наименьшее количество операций, необходимых для получения идеального организма.
1 2 1 3 1 3 2 4 5
2
Перестановку генов следует выполнить таким образом, чтобы одинаковые гены располагались рядом. При этом необходимо минимизировать количество выполненных операций.
Каждому натуральному числу поставим в соответствие отрезок , где — позиция, на которой встречается впервые, а — позиция, на которой встречается в последний раз.
Пусть — наибольшее возможное количество непересекающихся отрезков. Это значение можно найти с помощью алгоритма "выбора заявок", что означает, что каждый из оставшихся отрезков пересекается хотя бы с одним из выбранных. После этого нужно выполнить описанную процедуру над генами, которым эти отрезки соответствуют.
Если — общее количество построенных отрезков. Тогда ответом на задачу будет значение .
Пример
Для приведенного примера отрезки будут иметь вид (нумерация позиций начинается с нуля):
для ;
для ;
для ;
для ;
для ;
Отсортируем отрезки по координате конца:
Максимальное количество непересекающихся отрезков равно . Например, можно выбрать отрезки . Для генов, соответствующих оставшимся отрезкам, следует выполнить процедуру, описанную в условии задачи. К таким генам относятся и . Две перестановки можно выполнить, например, следующим образом:
Пусть — максимальное возможное количество геномов во входной последовательности.
#define MAX 100001
Вектор пар хранит отрезки .
vector<pair<int, int> > v; int s[MAX], e[MAX];
Читаем входные данные. Инициализируем .
scanf("%d", &n); memset(s, -1, sizeof(s)); memset(e, -1, sizeof(e)); for (i = 0; i < n; i++) { scanf("%d", &x);
Если число встречается впервые, то его позицию во входной последовательности записываем в .
if (s[x] == -1) s[x] = i;
Если число встречается не в первый раз, то его позицию записываем в , считая, что встречается в последний раз.
if (i > e[x]) e[x] = i; }
Все построенные отрезки заносим в вектор .
for (i = 1; i <= n; i++) // numbers 1..n if (s[i] != -1) v.push_back(make_pair(e[i], s[i]));
Отрезки заносятся в вектор в обратном порядке (в виде ), чтобы по умолчанию сортировка отрезков происходила в порядке возрастания их концов.
sort(v.begin(), v.end());
Находим максимальное количество непересекающихся отрезков.
i = res = 0; while (i < v.size()) {
Добавляем -й отрезок во множество непересекающихся отрезков. Переменная содержит конец этого отрезка (назовём его текущим отрезком).
res++; temp = v[i++].first; // end
Пока начало следующего отрезка меньше конца текущего, пропускаем такой отрезок, так как он пересекается с текущим.
while (i < v.size() && v[i].second < temp) i++; }
Выводим ответ.
printf("%d\n", v.size() - res);
Армия Речи Посполитой направляется из города Кострома в деревню Домнино. Во главе армии стоят два гетмана — Стефан и Константин.
Стефан располагает картой Костромской области, на которой указаны все дороги между деревнями. Каждую ночь он ведёт армию от одной деревни к другой по одной из этих дорог. Константин, в свою очередь, раздобыл карту с секретными тропами и днём ведёт армию по одной из них. Перед каждым переходом каждый из гетманов спрашивает у проводника Ивана Сусанина, какой маршрут лучше выбрать.
На карте Стефана указана длина каждой дороги, поэтому он может определить кратчайшее расстояние от любой деревни до деревни Домнино. Аналогично, Константин знает кратчайшие пути по своей карте троп.
Иван Сусанин, являющийся тайным агентом, старается не вызывать подозрений. Поэтому каждый раз он выбирает такую дорогу (для Стефана) и такую тропу (для Константина), чтобы кратчайшее расстояние до деревни Домнино по соответствующей карте строго уменьшалось.

Помогите Ивану определить, какой максимальной длины может быть маршрут до деревни Домнино.
Вход. Первая строка содержит три целых числа и — количество деревень в Костромской области, номер начальной деревни и номер деревни Домнино соответственно. Деревни пронумерованы от до . Гарантируется, что .
Затем следуют два блока, каждый из которых описывает карту: первый — карту Стефана, второй — карту Константина.
Первая строка каждого блока содержит целое число — количество дорог или троп. Каждая из следующих строк содержит три целых числа и , описывающих соединение между деревнями и длиной .
Передвигаться по дорогам и тропам можно в любом направлении. Гарантируется, что по каждой карте можно добраться из любой деревни в любую другую. Армия начинает движение вечером из деревни , проходя одну дорогу за ночь и одну тропу за день.
Выход. Выведите одно целое число — максимально возможную длину маршрута до деревни Домнино (учитывая чередование дороги и тропы). Если Иван Сусанин может водить армию бесконечно, не приводя её в Домнино, выведите "".
5 1 5 5 1 2 2 1 4 2 2 3 1 3 4 1 5 3 1 4 1 2 2 2 4 2 2 3 1 2 5 2
-1
3 1 3 4 1 2 10 2 3 10 1 3 20 2 3 30 4 2 1 10 1 3 10 1 1 10 2 3 10
20
Построим два графа: граф дорог и граф троп . Далее вычислим кратчайшие расстояния от деревни Домнино до всех деревень:
По карте Стефана: — минимальное расстояние от до деревни ;
По карте Константина: — минимальное расстояние от до деревни ;
Затем рассмотрим граф, вершинами которого являются пары :
— номер деревни;
(ночь, передвижение по дорогам), (день, передвижение по тропам).
Армия начинает путь вечером из начальной деревни, двигаясь ночью по дороге . Запускаем поиск в глубину из вершины по следующим правилам:
из (ночь) идём по дороге в , если ;
из (день) идём по тропе в , если .
Находясь в состоянии , перебираем только допустимые переходы, то есть такие, при которых расстояние до Домнино строго уменьшается. Среди них ищем путь максимальной длины.
Напомним, что каждый из гетманов стремится двигаться так, чтобы расстояние по его карте уменьшалось (при этом он не обязан следовать кратчайшему пути).
При выполнении поиска в глубину будем поддерживать следующую динамику:
— максимальная длина пути от до , начиная с типа .
Для каждого допустимого перехода:
Рекурсивно вызываем — переход в противоположную фазу.
Обновляем значение :
Таким образом, мы выбираем наилучший следующий ход, который приводит к максимальной суммарной длине пути. Это аналог стандартного поиска самого длинного пути в DAG (ориентированном ациклическом графе), где направление рёбер определяется уменьшением кратчайшего расстояния.
Движение разрешено только по рёбрам, ведущим к вершинам с меньшим расстоянием до Домнино. Однако в задаче присутствуют два графа – два типа рёбер: дороги и тропы, – а переходы между ними чередуются: ночь (дорога), день (тропа), ночь, день и так далее. Именно из-за этого чередования карт (дорог и троп) может возникнуть цикл между вершинами, даже несмотря на то, что в каждом отдельном графе (по одной карте) движение всегда идёт по убывающему расстоянию.
Если при поиске в глубину мы вновь попадаем в вершину, которая уже находится в стеке обработки, это означает, что обнаружен цикл.
Объявим константы.
#define INF 2e18 #define MAXN 1005
Объявим используемые структуры данных:
и — списки смежности для графов дорог и троп соответственно.
и — кратчайшие расстояния от деревни Домнино до всех остальных деревень.
и — максимальная длина пути от вершины до , если стартовать из по дороге или по тропе .
и — метки вершин при обходе в глубину для графов дорог и троп соответственно.
vector<pair<int, int>> g[2][MAXN]; long long d[2][MAXN]; long long dp[2][MAXN]; int used[2][MAXN];
Функция dijkstra вычисляет кратчайшие расстояния от деревни Домнино до всех остальных деревень либо по дорогам , либо по тропам .
void dijkstra(int type) { for (int i = 1; i <= n; i++) d[type][i] = INF; d[type][t] = 0; priority_queue<pair<long long, int>, vector<pair<long long, int>>, greater<pair<long long, int>>> pq; pq.push({ 0, t }); while (!pq.empty()) { pair<long long, int> top = pq.top(); pq.pop(); long long dist = top.first; int v = top.second; if (dist > d[type][v]) continue; for (auto edge : g[type][v]) { int to = edge.first; int w = edge.second; if (d[type][to] > d[type][v] + w) { d[type][to] = d[type][v] + w; pq.push({ d[type][to], to }); } } } }
Функция dfs выполняет поиск в глубину и находит самый длинный путь при движении по чередующимся картам, начиная из деревни по дороге или по тропе . Если при таком чередующемся движении возникает возможность попасть в цикл, функция возвращает .
void dfs(int v, int type) {
Вершина в графе становится серой.
used[type][v] = 1;
Инициализируем динамику.
dp[type][v] = 0;
Перебираем ребра, исходящие из вершины в графе .
for (auto edge : g[type][v]) { int to = edge.first; int weight = edge.second;
Переход из вершины в вершину возможен только в том случае, если расстояние от до Домнино меньше, чем расстояние от до Домнино.
if (d[type][to] < d[type][v]) {
Если вершина в графе уже находится в процессе обхода (помечена как "серая"), значит, обнаружен цикл — возвращаем .
if (used[type ^ 1][to] == 1) { cout << -1 << endl; exit(0); }
Если вершина в графе еще не посещена, продолжаем из нее поиск в глубину.
if (used[type ^ 1][to] == 0) dfs(to, type ^ 1);
После обработки всех допустимых переходов пересчитываем динамику, выбирая максимальную длину пути от вершины в графе до Домнино.
dp[type][v] = max(dp[type][v], dp[type ^ 1][to] + weight); } }
Вершина в графе помечается черной.
used[type][v] = 2; }
Основная часть программы. Читаем входные данные. Строим графы дорог и троп.
scanf("%d %d %d", &n, &s, &t); scanf("%d", &m1); for (i = 0; i < m1; i++) { scanf("%d %d %d", &a, &b, &len); g[0][a].push_back({ b, len }); g[0][b].push_back({ a, len }); } scanf("%d", &m2); for (i = 0; i < m2; i++) { scanf("%d %d %d", &a, &b, &len); g[1][a].push_back({ b, len }); g[1][b].push_back({ a, len }); }
Вычисляем кратчайшие расстояния от деревни Домнино до всех остальных деревень — отдельно для карты дорог и для карты троп.
dijkstra(0); dijkstra(1);
При помощи поиска в глубину находим самый длинный путь при движении по чередующимся картам, начиная из деревни по дороге.
dfs(s, 0);
Выводим ответ. Стартовая точка — деревня , движение начинается по дороге.
printf("%lld\n", dp[0][s]);
Алиса — фокусница, и она придумала новый трюк. У неё есть карточек с различными числами от до , написанными на них. Сначала она просит одного из зрителей перетасовать колоду и выложить карты в ряд. Допустим, что на -й карте слева стоит число .
Затем Алиса выбирает две перестановки и . На и есть ограничение — у перестановок не должно быть фиксированных точек. То есть и .
После выбора перестановок Алиса перемешивает карты в соответствии с ними. Теперь -ой картой слева является карта . Трюк считается успешным, если на -ой карте слева после тасования стоит число .
Помогите Алисе выбрать перестановки и или скажите, что это невозможно для заданной перестановки .
Вход. Первая строка содержит количество тестов .
Каждый тест задается двумя строками. Первая строка содержит одно целое число — количество карточек. Вторая строка содержит целых чисел — начальную перестановку карточек.
Гарантируется, что сумма по всем тестам не превышает .
Выход. Выведите ответ для каждого теста в том же порядке, в котором они появляются во входных данных.
Для каждого теста в отдельной строке выведите "Impossible", если решение не существует.
В противном случае, в первой строке выведите "Possible", а в следующих двух строках выведите перестановки и .
4 2 2 1 3 1 2 3 4 2 1 4 3 5 5 1 4 2 3
Impossible Possible 3 1 2 2 3 1 Possible 3 4 2 1 3 4 2 1 Possible 4 1 2 5 3 3 1 4 5 2
Рассмотрим три перестановки: . Пусть — тождественная перестановка. Рассмотрим равенство: . Отсюда . Используя тот факт, что перестановка применяется справа налево, указанное тождество можно переписать в виде: . Отсюда следует что .
Генерируем случайную перестановку , у которой нет фиксированных точек. После чего вычисляем перестановку и тоже проверяем, чтобы у нее не было фиксированных точек. Найденные перестановки и являются искомыми.
Пример
Рассмотрим перестановку .
Обратной к ней будет .
Генерируем случайную перестановку без фиксированных точек. Пусть, например, . Вычислим перестановку, ей обратную: .
Далее вычисляем . Перестановка не содержит фиксированных точек, поэтому она нам подходит.
Функция print выводит элементы массива , начиная с первого.
void print(vector<int>& a) { for (int i = 1; i < a.size(); i++) printf("%d ", a[i]); printf("\n"); }
Функция is_valid проверяет, содержит ли перестановка фиксированные точки.
bool is_valid(vector<int>& v) { for (int i = 1; i < v.size(); i++) if (v[i] == i) return false; return true; }
Объявим рабочие массивы.
vector<int> a, p, p1, q;
Основная часть программы. Читаем входные данные.
scanf("%d", &tests); while (tests--) { scanf("%d", &n);
Инициализируем массивы.
a.resize(n + 1); p1.resize(n + 1); p.resize(n + 1); q.resize(n + 1);
Читаем входную перестановку и сразу в массив заносим перестановку, ей обратную. То есть массив содержит перестановку , где — входная перестановка.
for (i = 1; i <= n; i++) { scanf("%d", &x); a[x] = i; }
mt19937 — это один из стандартных генераторов псевдослучайных чисел в C++ (Mersenne Twister с периодом 19937). Он очень быстро работает и обладает хорошими статистическими свойствами, что делает его отличным выбором для большинства задач, требующих генерации случайных чисел.
mt19937 gen(random_device{}());
В переменной будем отмечать, найден ли ответ для текущего теста.
found = false;
Проводим итераций.
for (it = 0; it < 1000; it++) {
Генерируем случайную перестановку. Для этого заносим в массив числа и при помощи функции shuffle проводим перемешивание элементов, начиная с первого (не нулевого) элемента. Мы работаем с перестановками от до .
iota(p.begin(), p.end(), 0); shuffle(p.begin() + 1, p.end(), gen);
Проверяем, является ли перестановка допустимой (не содержит фиксированных точек).
if (!is_valid(p)) continue;
Вычисляем перестановку .
for (i = 1; i <= n; i++) p1[p[i]] = i;
Вычисляем перестановку .
for (i = 1; i <= n; i++) q[i] = p1[a[i]];
Если перестановка является допустимой, то выводим ответ.
if (is_valid(q)) { puts("Possible"); print(p); print(q); found = true; break; } }
Если за итераций ответ не был найден, то его не существует.
if (!found) puts("Impossible"); }