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

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

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

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

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

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

Задача 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
  • Проголосовать: не нравится

14 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится
спс за разбор. А может кто подкинуть ссылку на теорию последней задачи ?
  • 14 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    А можно ли ещё и увидеть авторское решение последней задачи ?
    • 14 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится
      Про сильно связные компоненты можно почитать в Кормене.
    • 14 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится
      Если сообщество не возражает против обмена решениями, я могу дать свое. Обращайтесь в личку.
      • 14 лет назад, # ^ |
          Проголосовать: нравится 0 Проголосовать: не нравится
        Есть алгоритм для решения E в случае произвольного графа ?
        • 14 лет назад, # ^ |
            Проголосовать: нравится 0 Проголосовать: не нравится
          Я пытался написать. WA #10. Не знаю, то ли бага в решении, то ли в идее. Может частный случай какой-то...
        • 14 лет назад, # ^ |
            Проголосовать: нравится 0 Проголосовать: не нравится
          На правах рекламы
          http://acm.mipt.ru/judge/problems.pl?problem=098
14 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

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

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

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

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

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

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

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

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

  • 14 лет назад, # ^ |
      Проголосовать: нравится +1 Проголосовать: не нравится
    У нас остались непокрашенные вершины и мы не знаем, сколько различных циклов они образуют. Поэтому мы запускаем обход из некоторой вершины и получается, что мы красим весь цикл, т.е. выделяем компоненту. И так пока все вершины не покрасим.
14 лет назад, # |
  Проголосовать: нравится 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);
}

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

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

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

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

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

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

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

      на 

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

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

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

14 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится
Обидно как! Была идея на третью задачу точно такая же, как и в разборе, а не сдал. Не правильно расчитал диапозон рёбер, ну в смысле [(n-1)*(n-2)/2+1] вот эту формулу написал неверно(((((((((((((.
14 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится
Обидно как! Была идея на третью задачу точно такая же, как и в разборе, а не сдал. Не правильно расчитал диапозон рёбер, ну в смысле [(n-1)*(n-2)/2+1] вот эту формулу написал неверно(((((((((((((.
14 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится
Обидно как! Была идея на третью задачу точно такая же, как и в разборе, а не сдал. Не правильно расчитал диапозон рёбер, ну в смысле [(n-1)*(n-2)/2+1] вот эту формулу написал неверно(((((((((((((.
14 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится
странно что 3 раза отправилось, сори.
14 лет назад, # |
  Проголосовать: нравится +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
  • 14 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    I am not solving E with tutorial's method, and I got Wrong Answer on test 15 for a long time. Did U get any trouble on test 15? Thanks.
    • 14 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится
      http://piratepad.net/ZosxPMMqyK

      There's my code for it. It passes 15, but gets RE on Test 22. I'm really not sure why. Tell me if you can find it, and hopefully it helps you with yours!
      • 14 лет назад, # ^ |
          Проголосовать: нравится +2 Проголосовать: не нравится
        You have stack overflow in dfs. Stack for default thread in java is not very big, you can avoid it by creating your own thread and executing code in it, like
        class Sample implements Runnable {
        public static void main(String[] args) {
        new Thread(new Sample()).start();
        }

        public void run() {
        //Solution here
        }
        }

        • 14 лет назад, # ^ |
            Проголосовать: нравится 0 Проголосовать: не нравится
          Thanks (to Ivan too)!

          I really shouldn't have missed that... though I didn't know that a new Thread could get extra stack space. Most interesting.

          I've got AC now and I've updated my code :D
      • 14 лет назад, # ^ |
          Проголосовать: нравится +1 Проголосовать: не нравится
        new Thread(null, new Sample(), "1", 1<<23).start(); - 8 megabytes stack.
14 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится
problem C may also be solved using a stack + dp, which gives an extremely fast O(mn) solution
  • 14 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    *problem B

    ps: there really should be an edit function for comments
    • 14 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится
      Could you give more details for the O(mn) solution?
      • 14 лет назад, # ^ |
          Проголосовать: нравится 0 Проголосовать: не нравится
        its a fairly standard solution
        its the same as the "largest rectangle in a histogram" problem except now you're looking for maximal perimeter, and you need to do a dp preprocessing
        the "largest rectangle in a histogram" solution is here: http://homeofcox-cs.blogspot.com/2010/04/max-rectangle-in-histogram-problem.html
    • 6 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится

      I have written this O(m*m*n) solution for problem B https://mirror.codeforces.com/contest/22/submission/54368605 I am unable to think of any better approach. Can you please provide working code for O(m*n) solution. Thanks in Advance.

      • 4 года назад, # ^ |
          Проголосовать: нравится 0 Проголосовать: не нравится

        praveenojha33 Can you please give a short explanation of your code. Thanks in advance!

        • 4 года назад, # ^ |
            Проголосовать: нравится 0 Проголосовать: не нравится

          Do you know about maximum submatrix sum in a 2D matrix using Kadane Algorithm. If not you can watch it here https://www.youtube.com/watch?v=yCQN096CwWM

          The problem is same as finding submatrix sum in 2D matrix this can be done using either O(n^4) approach using 2D prefix sum or using Kadane Algorithm in O(n^3). As you know Kadane Algorithm is O(n) and in 2D Kadane Algorithm we divide 2D matrix in n^2 submatrices that is from column (1-1),(1-2),(1-3),(1-4),(2-2),(2-3),(2-4),(3-3),(3-4),(4-4) and then apply kadane Algorithm to find max sum in that rectagular strip.

14 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится
the complexity of my solution of problem B is O(m^2 * n).
»
12 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

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

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

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

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