Освітній раунд №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");
}