Блог пользователя netman

Автор netman, история, 8 лет назад, По-русски
Tutorial is loading...
Code
Tutorial is loading...
Code
Tutorial is loading...
Code
Tutorial is loading...
Code
Tutorial is loading...
C++ code
Java code

Полный текст и комментарии »

Разбор задач Codeforces Round 390 (Div. 2)
  • Проголосовать: нравится
  • +108
  • Проголосовать: не нравится

Автор netman, история, 8 лет назад, По-русски

Привет, Codeforces!

Рад сообщить, что сегодня в 17:35 MSK состоится первый в новом году раунд Codeforces Round #390 для второго дивизиона. Традиционно, участники из первого дивизиона смогут принять участие вне конкурса.

Подготовкой раунда занимался я, Алексей netman Вистяж.

Выражаю благодарность Николаю KAN Калинину за помощь в подготовке задач и Михаилу MikeMirzayanov Мирзаянову за платформы Codeforces и Polygon.

Участникам будет предложено пять задач и два часа на их решение.

Разбалловка будет объявлена к началу раунда.

UPD: Разбалловка 500 — 1000 — 1500 — 2000 — 2500

UPD2:

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

Поздравляем победителей!

Div 2:

  1. i_wanna_no_exams_fluever
  2. markysha
  3. Yamada
  4. LLI_E_P_JI_O_K
  5. I_love_livlivi
  6. wolf29
  7. sunjam
  8. AkaneSasu
  9. GemaSamuca
  10. EliteWantsYou

Div 1:

  1. kraskevich
  2. vintage_Vlad_Makeev
  3. uwi
  4. halyavin
  5. sugim48

UPD3:

Разбор опубликован!

Полный текст и комментарии »

  • Проголосовать: нравится
  • +252
  • Проголосовать: не нравится

Автор netman, 10 лет назад, По-русски

456A - Ноутбуки

Решение: 7407613;

В этой задаче надо было проверить существования таких i и j, что i ≠ j, a[i] < a[j], b[i] > b[j]. Если существуют такие i и j, то Леша рад.

Для этой задачи есть очень простое решение. Просто проверить, что для всех i верно, что a[i] = b[i]. Если это так, то Леша не рад.

Доказательство очень простое. Давайте отсортируем массивы a и b, как пары чисел, по возрастанию. Можно увидеть что Лёша рад, если есть хотя бы одна инверсия в массиве b, то есть существуют такие i и j, что b[i] > b[j] и i < j (, так как числа в массиве a идут в возрастающем порядке). То есть это значит, что массив b не отсортирован, и это значит, что a ≠ b.

456B - Федя и математика

Решения: 7407625, 7407631;

В этой задаче надо было вычислить то, что просили в условии. Правда в лоб это не легко сделать.

Но если посмотреть на формулу, то можно сделать следующее:

Эта формула верна, потому что 5 — простое число и оно взаимнопросто с 1, 2, 3, 4.

φ(5) = 4

Чтобы применить это решение, нужно уметь брать остаток от деления на 4 длинного числа и уметь вычислять выражение для маленького n.

Асимптотика — .

Можно было писать другое решение. Более лобовое. Можно было использовать быстрое возведение в степень, но не бинарное. Идея та же, что и у бинарного возведения. Пусть мы возводим число x в степень n по модулю P. Мы должны двигаться по длинному числу n с конца и хранить два значения: Result — текущий результат и K — число x возведенное в 10i, где i — номер разряда на котором мы стоим (разряды нумеруются с конца начиная с нуля). Чтобы обновить ответ надо сделать следующее: , c[i]i-ая цифра числа n с конца.

Асимптотика — .

456C - Скука / 455A - Скука

Решения: 7407649, 7407655;

В этой задаче вам надо было максимизировать сумму набранных очков. Давайте подсчитаем количество всех чисел в массиве a, то есть заведем массив cnt, где cnt[x] — количество чисел x в массиве a. Теперь очень просто считать ДП. Оно имеет такой вид:

f(i) = max(f(i - 1), f(i - 2) + cnt[ii), 2 ≤ i ≤ n;

f(1) = cnt[1];

f(0) = 0;

Ответ будет содержаться в f(n).

Асимптотика — O(n).

456D - Много игр / 455B - Много игр

Решения: 7407663, 7407670;

Давайте научимся определять, может ли игрок выиграть и может ли проиграть в одной игре. Для этого нам понадобится префиксное дерево(бор), сформированние из всех строк набора. Будем считать два ДП — win[v], lose[v]. Если v — лист бора, то win[v] = false;lose[v] = true.

Иначе win[v] = (win[vor (not win[i])); lose[v] = (lose[vor (not lose[i])), по всем i — детям вершины v.

Теперь осталось рассмотреть пару случаев:

Если win[v] = false, то выигрывает второй игрок (то есть первый игрок будет все k игр проигрывать и начинать заново новую игру, так как он проиграл в прошлой игре).

Если win[v] = true и lose[v] = true, то выигрывает первый игрок (тут первый игрок может в первой игре изменить ход событий в свою пользу).

Иначе у нас остается только один случай — win[v] = true и lose[v] = false, то тут исход игры зависит от четности k, если k mod 2 = 1, то выигрывает первый игрок, иначе второй (в этом случае игроки будут по очереди выигрывать и всё зависит от чётности k).

Асимптотика — .

456E - Цивилизация / 455C - Цивилизация

Решения: 7407681, 7407683;

Во-первых можно было заметить, что наша система дорог всегда будет оставаться лесом деревьев. Для эффективного хранения компонент следует использовать СНМ (система непересекающихся множеств). Вначале нужно построить начальную систему дорог. В каждой компоненте начальной системы дорог нужно найти диаметр компоненты. Это можно сделать обходом в ширину или глубину. Пусть a — произвольная вершина. Тогда b — самая далекая вершина от вершины a. И c — самая далекая вершина от вершины b. Расстояние от вершины b до вершины c — диаметр. Все эти утверждения верны только для дерева. Теперь для каждого множества в СНМ мы знаем его диаметр. Теперь очень легко отвечать на запрос первого типа: Узнать в каком множестве лежит вершина x и вывести диаметр этого множества. Запрос второго типа тоже легко обработать: Пусть u — номер множества в котором лежит вершина x, v — номер множества в котором лежит вершина y. Теперь если u ≠ v, то можно объеденять эти два множества в новое множество. Диаметр нового множетсва вычисляется так:

Асимптотика — O(n·A - 1(n)), где A - 1(n) — обратная функция Аккермана.

455D - Серега и веселье

Решения: 7407693, 7407699, 7407703;

Давайте сведем запрос 1-го типа к двум более простым более простым запросам:

Удалить число из массива на r-ой позиции. Вставить это же число в массив на l-ую позицию, то есть после (l - 1)-ой позиции.

Теперь давайте хранить наш массив, как блоков. В каждом блоке будем хранить сами числа в таком порядке, как в массиве a и будем хранить массив cnt. cnt[x] — количество чисел x в блоке. Всё это будет требовать памяти.

Теперь легко быстро выполнять запрос 1-го типа. Удалить число с r-ой позиции мы можем за операций. А потом уже вставить это же число тоже можем за операций. За O(1) мы так же можем обновлять массив cnt. Асимптотика выполнения первого запроса — .

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

Чтобы сохранять размеры блоков близкими к , нужно каждые запросов 1-го вида перестраивать наши блоки. Перестройка делается за O(n).

Итоговая асимптотика — .

455E - Функция

Решения: 7407711, 7452418;

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

Можно свести задачу нахождения значения функции f(x, y) к более простой задаче:

Пройти по массиву a, начиная от позиции y, сделав (x - 1) шаг. Шаг может быть таким: либо пойти влево, либо остаться на месте.

Значение функции , где ki — сколько раз мы посетили i-ый элемент массива a.

Для фиксированного l понятно, выгоднее всего, чтобы минимум на отрезке [l, y] был посещен (x - (y - l)) раз, а все остальные числа по одному разу. Ещё можно заметить, что нам выгодно, чтобы a[l] был минимумом.

Из всего этого можно сделать вывод, что для фиксированного l ответом будет — sum[y] - sum[l] + a[l]·(x - (y - l)), где sum — массив префиксных сумм массива a.

Формулу выше можно расписать так:

sum[y] - sum[l] + a[l]·(x - (y - l)) = sum[y] - sum[l] + a[l]·(x - y + l) = sum[y] - sum[l] + a[ll + a[l]·(x - y) = sum[y] + (a[l]·(x - y) + a[ll - sum[l])

Можно заметить, что в скобках вышло что-то похожее на уравнение прямой — K·X + B. В скобках вышло: a[l]·(x - y) + a[ll - sum[l], где K = a[l], X = (x - y), B = a[ll - sum[l].

Теперь нам осталось научиться для всех подходящих l научиться искать минимум на всех прямых при фиксированном X = (x - y).

Прямых у нас будет n. То есть для каждого элемента массива a своя прямая. Теперь ответ на запрос будет равен:

, где (Ki, Bi)i-ая прямая. Ki = a[i], Bi = a[ii - sum[i].

Чтобы вычислять ответ на запрос быстрее, чем простой проход по прямым, нужно ипользовать Convex Hull Trick на дереве отрезков. То есть в каждой вершине дерева отрезков мы храним все прямые, за которые отвечает эта вершина. Итого у нас будет израсходовано памяти. А запрос будет выполняться за операций. Так как мы посетим вершин и в каждой вершине выполним операций. Подробнее познакомиться с Convex Hull Trick вы можете тут.

Асимптотика — .

Полный текст и комментарии »

Разбор задач Codeforces Round 260 (Div. 1)
Разбор задач Codeforces Round 260 (Div. 2)
  • Проголосовать: нравится
  • +45
  • Проголосовать: не нравится

Автор netman, 11 лет назад, По-русски

Всем привет!

Не так давно я решил задачу COT2.

В этой задаче нам дано дерево. Каждому дереву соответствует число. Нужно быстро отвечать на запрос x y — кол-во различных чисел на пути от вершины x до вершины y.

Сначала я написал на неё, как мне казалось верное решение, но оно было не верным, но оно зашло.

Вот это решение — http://pastie.org/8667428.

Идея его была такова:

Обойдем дерево dfs'ом и сохраним все вершины в порядке обхода. Обход назовем v[]. Потом чуть переделаем наши запросы:

Вместо (x,y) будет (l,r), где l и r позиции вершин x и y в нашем обходе v[]. Если l>r, то поменяем l и r местами.

Теперь применим корневую эвристику к запросам, то есть отсортируем запросы по паре (l/block,r), где block ~ sqrt(n).

Теперь будем обрабатывать наши запросы в отсортированном порядке.

То есть, если мы сейчас на пути v[old_l]->v[old_r] и нам надо перейти на путь v[l]->v[r], то мы просто двигаемся по дереву от вершины old_l до вершины l и от вершины old_r до вершины r. При этом, каждый раз посещая какую-нибудь вершину, мы проверяем: лежит ли она на пути v[l]->v[r], если лежит, то проверяем ложили ли мы число этой вершины в ответ, если нет, то ложим(ложить будем числа за O(1) применяя обычный массив меток).

Это есть мое первое решение. Оно зашло, хоть оно неверное. Так как для него есть хороший контр-тест, который дает асимптотику O(N*M).

Этот тест будет таким:

    1
   /|\
  / 2 \
 |     |
 |     |
 .     .
 .     .
 .     .
(n/2) (n)

А запросы будут такого вида:

(n/2,n/2+1), (2,n/2+2), (n/2,n/2+3), ..., (2,n)

Это будет худшим случаем для данного решения, так как мы построим такой обход:

v[]={ 1, 3, 4, 5, ..., n/2, 2, n/2+1, n/2+2, ..., n }

И вершины n/2 и 2 будут находиться рядом, но вот расстояние между ними будет O(N), и если мы будем обрабатывать запросы, в которых мы должны будем переходить на такое расстояние, то будет сложность решения O(N*M).

Но чуть позже я придумал верное решение. Оно отличается от первого решения совсем немного.

Вот код этого решения — http://pastie.org/8667490.

Мы должны поменять обход, чтобы расстояние между соседними вершинами в обходе было O(1). То есть dist(v[i],v[i+1])=O(1).

Делать будем его так:

Почти так же, как и обычный обход.

Будем ходить dfs'ом по дереву, когда заходим в вершину, то добавляем её в обход, если она на четной глубине. Когда выходим из вершины, то добавляем её в обход, если она на нечетной глубине.

Из этого видно что расстояние между соседними вершинами будет примерно 3-4, то есть dist(v[i],v[i+1])=O(1).

И мы получаем честное решение за O(M sqrt N).

Можете рассматривать эту мини-статейку, как разбор к этой задаче :)

Но у меня есть вопрос: Можно ли решить эту задачу с более лучшей асимптотикой, например O(M log N) или O(M log^2 N)? Если да, то как её решить с более лучшей асимптотикой?

Полный текст и комментарии »

  • Проголосовать: нравится
  • +16
  • Проголосовать: не нравится

Автор netman, 11 лет назад, По-русски

Собираюсь купить себе ноутбук для спортивного программирования(до 600$), только не знаю какой выбрать.

Нужны примерно такие характеристики:

  1. Видеокарта: не нужна;

  2. Процессор: можно i3;

  3. Экран: вот тут я не знаю что выбрать(13.3" или 15.6");

  4. Оперативная память: 3-4GB думаю хватит;

  5. Жесткий диск: хватит 320GB;

  6. Клавиатура: нужна удобная, чтобы печатать было быстро и удобно;

  7. Батарея: нужно чтобы держала в кодерском режиме 3-4 часа;

Пожалуйста помогите выбрать ноутбук под мои нужды.

Не минусуйте пожалуйста :)

UPD:

Я подобрал пару моделей. Но какая из них лучше, надежнее и удобнее для кодинга?

  1. Lenovo IdeaPad Z580 (Lenovo все хвалят из-за качества, посматриваю на этот ноут)

  2. Acer Aspire V3-571 (Acer'ы наоборот многие не любят за плохое качество, но эта модель хороша)

  3. ASUS K55A (этот ноутбук тоже неплох, может стоит его выбрать?)

Полный текст и комментарии »

  • Проголосовать: нравится
  • +46
  • Проголосовать: не нравится