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

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

Обсуждение контеста

Задача А. Вторая порядковая статистика

Тематика: Сортировка

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

Задача B. Стол для переговоров

Тематика: Симуляция, динамическое программирование

В этой задаче требовалось определить максимальный периметр прямогульника, который не содержит внутри себя единиц. Назовем прямоугольники, не содержащие единиц, допустимыми. Для решения надо было просмотреть все допустимые прямоугольники и выбрать максимальный периметр. Вопрос в том, как перебирать все допустимые прямоугольники. Говорят, что решение "в лоб" с помощью 6 вложенных циклов за 256 проходит по времени. Очевидно, четыремя циклами мы фиксируем углы прямоугольника, а еще двумя проверяем его допустимость. Не совсем очевидно, что такое решение уложится по времени.

Можно было написать более аккуратное решение с помощью динамического программирования за O((n*m)2). Заметим, что прямоугольник с координатами (x1, y1, x2, y2) является допустимым тогда и только тогда, когда допустимыми явлюятся прямоугольники (x1, y1, x2-1, y2), (x1, y1, x2, y2-1) и в клетке (x2, y2) стоит '0'. Таким образом, пересчет допустимости производится за O(1), что суммарно будет происходить за время O((n*m)2), т.е. линейное от количества прямоугольников. 

Задача C. Системный администратор

Тематика: Симуляция

В этой задаче требовалось составить связный граф из n вершин, содержащего m ребер, такой, что при удалении вершины v граф перестанет быть связным или сообщить, что такой граф не существует. При этом не должно быть кратных ребер. Очевидно, что связный граф не может существовать, если число ребер меньше, чем n-1. Кроме того, нетрудно видеть, что максимальное число ребер достигается, когда по одну стороны от v находится одна вершина, а по другую - полный граф из оставшихся вершин. Это число равно (n-1)*(n-2)/2+1. Если число ребер лежит в допустимом диапазоне - граф всегда существует. Дальше нужно было аккуратно разместить одну вершину по одну сторону от v (пусть это вершина 1), а с другой стороны построить связный граф с m-1 ребром. Лучше всего это было сделать проведя сначала ребра из v во все остальные, кроме 1, а затем между всеми остальными (кроме 1).

Задача D. Отрезки

Тематика: Метод сканирующей прямой

В этой задаче требовалось вбить наименьшее число гвоздиков в отрезки так, чтобы каждый отрезок касался хотя бы одного гвоздика. Назовем координату конца отрезка событием. Наши события будут двух типов: начало отрезка и конец отрезка. Отсортируем события в порядке возрастания координат. В случае равенства координат раньше будут идти события начала отрезка. Теперь будем смотреть события в соответствии с заданным порядком: если встретили начало отрезка, добавим его номер в список необработанных отрезков. Как только встретили событие конца отрезка, который еще не обработан, вбиваем туда гвоздь и очищаем список (т.е. считаем, что все отрезки из списка обработаны, т.е. прибиты).

Задача E. Схема

Тематика: Теория графов

В задаче E дан ориентированный граф, требовалось сделать его сильно связным, т.е. чтобы каждая вершина была достижима из любой другой, добавив минимальное число ребер. Из формата входных данных неявно следует очень важное условие: у любой вершины есть не более одного исходящего ребра. Значит, начав путь из некоторой вершины мы либо зациклимся, либо нам некуда будет идти. Отсюда следует, что любая компонента связности (обычной) имеет вид либо замкнутого цикла, либо несколько простых путей, входящих в вершину или цикл. Сначала рассмотрим все пути, выходящие из вершин, у которых нет входящих ребер. Проходя, будем красить вершины, пока либо нам некуда будет идти, либо придем в покрашенную вершину. Тогда вершина, из которой мы пошли, будет называться началом, а в которую пришли - концом компоненты.

После этого рассмотрим еще непокрашенные вершины - они точно принадлежат циклам. Началом и концом цикла назовем некоторую вершину, ему принадлежащую. Получили набор компонент, которые надо соединить. Будем соединять циклически: из конца i-й компоненты пустим ребро в начало ((i+1)%k)-й, где k - число компонент. Ответом будет k. Здесь есть исключение: если у нас только одна компонента, формирующая цикл, то ответ 0.

Таким образом каждое ребро мы просмотрим один раз и время работы составит O(n).


Upd: 

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

Говорят, что в задаче B решение с шестью вложенными циклами работает за 30 мс, т.е. с ним нет никаких проблем.

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

16 лет назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится
спс за разбор. А может кто подкинуть ссылку на теорию последней задачи ?
16 лет назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится

В D оказывается достаточно простое решение.

Я ее сделал так:

Нашел максимальное множество непересекающихся отрезков (делается простым ДП). Размер этого множества равен кол-ву вбиваемых гвоздиков.

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

  • 16 лет назад, скрыть # ^ |
     
    Проголосовать: нравится +3 Проголосовать: не нравится
    Вроде как классическое решение такое.

    1) Добавляем в ответ правый конец такого отрезка, у которого правый конец самый левый
    2) Удаляем (игнорируем в будущем) все отрезки, которые пересекаются с добавленной точкой
    3) Если множество отрезков не пусто, переходим к 1)
    • 16 лет назад, скрыть # ^ |
       
      Проголосовать: нравится 0 Проголосовать: не нравится
      алгоритм жадный насколько я понимаю .. а как обосновать его правильность? что-то вроде удаляем отрезок с самым левым правым концом, т.к. этот отрезок всё равно надо как-то удалить, а гвоздь лучше в таком случае вбивать в его правый конец?
      • 16 лет назад, скрыть # ^ |
         
        Проголосовать: нравится 0 Проголосовать: не нравится
        пусть есть способ лучше. Реализуем его. Сдвинем все гвозди вправо так, чтобы не испортить принцип, т.е. будем двигать каждый гвоздь вправо до тех пор, пока условие задачи выполняется. Тогда где будет первый гвоздь? В самом левом правом конце - очевидно. А где следующий? И т.д.
  • 16 лет назад, скрыть # ^ |
     
    Проголосовать: нравится 0 Проголосовать: не нравится
    Вот ещё одно решение:
    1) Удалим отрезки внутри которых лежит хотя бы один другой отрезок (аккуратно с удалением одинаковых отрезков)
    2) Отсортируем отрезки в первую очередь по левому концу, во вторую по правому
    3) Будем поддерживать позицию последнего вбитого гвоздя и идти по отрезкам в порядке сортировки. Если последний вбитый гвоздь лежит внутри текущего отрезка, то пропускаем этот отрезок, иначе вбиваем гвоздь в правый край.
16 лет назад, скрыть # |
 
Проголосовать: нравится +5 Проголосовать: не нравится
Вообще кстати задача B решается за O(N*M).
Фиксируем левую границу (за N видимо), для каждой строки смотрим сколько справа нулей (это мы предпросчитали заранее), и теперь осталось решить задачу о поиске в массиве подмассива, для которого произведение длины на минимальный элемент максимально. А эта задача решается на M с помощью несложных махинаций со стеком.
16 лет назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится

не совсем понятно, что делать с незакрашенными вершинами...( которые точно относятся а циклу)....

можно немного конкретней ?

16 лет назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится

я наверно ужен надоел, но у меня не проходит 3 тест...уже 3 раза пытался сдать.... не могу понять : что не правильно ?!


может посмотрите ?


scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d",&a[i]),b[a[i]]=1;
for (int i=1;i<=n;i++) if (!b[i]){
j=i;
while (a[j]) x=a[j],a[j]=0,j=x;
w[++k].x=i,w[k].y=j;
}
for (int i=1;i<=n;i++) if (a[i]){
j=i;
while (a[j]) x=a[j],a[j]=0,j=x;
w[++k].x=w[k].y=i;
}
if (k==1 && w[1].x==w[1].y) puts("0");else{
printf("%d\n",k);
w[k+1].x=w[1].x,w[k+1].y=w[1].y;
for (int i=1;i<=k;i++) printf("%d %d\n",w[i].y,w[i+1].x);
}

  • 16 лет назад, скрыть # ^ |
     
    Проголосовать: нравится +1 Проголосовать: не нравится

    А вы проверяли ее на том тесте, который я написал? У меня, например, она на нем не работает. Немного подебажил, после строки

    w[++k].x=w[k].y=i;

    оказалось, что w[0].x = 0, w[0].y = 1, w[1].x = 1, w[1].y = 0. Вероятно, несколько равенств в строке и инкремент ведут себя некорректно.

    • 16 лет назад, скрыть # ^ |
       
      Проголосовать: нравится +1 Проголосовать: не нравится

      Большое Спасибо за помощь !!! Наконец - то АС . На том тесте,что давали вы, у меня , почему - то , работало.... В общем я исправил

      w[++k].x=w[k].y=i;

      на 

      w[++k].x=i , w[k].y=i; и прошло =)

      на олимпе с 4 - мя справился быстро, а с этой промучался почти час... Поэтому очень хотелось дорешать ....

      Ещё раз, БОЛЬШОЕ СПАСИБО =) 

16 лет назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится
Обидно как! Была идея на третью задачу точно такая же, как и в разборе, а не сдал. Не правильно расчитал диапозон рёбер, ну в смысле [(n-1)*(n-2)/2+1] вот эту формулу написал неверно(((((((((((((.
16 лет назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится
Обидно как! Была идея на третью задачу точно такая же, как и в разборе, а не сдал. Не правильно расчитал диапозон рёбер, ну в смысле [(n-1)*(n-2)/2+1] вот эту формулу написал неверно(((((((((((((.
16 лет назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится
Обидно как! Была идея на третью задачу точно такая же, как и в разборе, а не сдал. Не правильно расчитал диапозон рёбер, ну в смысле [(n-1)*(n-2)/2+1] вот эту формулу написал неверно(((((((((((((.
16 лет назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится
странно что 3 раза отправилось, сори.
16 лет назад, скрыть # |
 
Проголосовать: нравится +3 Проголосовать: не нравится
Some of your types seem incorrect... Problems B and C are not simulation. Simulation involves running some sort of given process (usually efficiently), and neither of these problems involve that. B is brute force / DP, and C is graph theory.

But the tutorial is great, thanks :) I can go fix my E now :D
16 лет назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится
problem C may also be solved using a stack + dp, which gives an extremely fast O(mn) solution
16 лет назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится
the complexity of my solution of problem B is O(m^2 * n).
»
13 лет назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится

Почему это решение 2858329 вызывает TL10 ?

  • »
    »
    13 лет назад, скрыть # ^ |
     
    Проголосовать: нравится +13 Проголосовать: не нравится

    Ну если внимательно посмотреть на тест, то можно догадаться, что после выполнения m -= n - 1 m становится равным нулю, поэтому в дальнейшем --m будет возвращать -1, -2, ...

    Наверное, фиксится исправлением if (--m <= 0) return;, по крайней мере, TL уже не будет.