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

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

Здравствуйте! Есть такая задача: Есть объекты, их нужно распределить на 2 множества. Вводятся условия типа эти 2 объекта точно лежат/не лежат в одно множестве. Нужно такие запросы обрабатывать в онлайн и проверять на противоречивость. И еще нужно уметь по запросу выводить корректный пример разбиения. Хочу предложить такой метод решения данной задачи: Создадим СНМ из 2*n множеств. Каждой вершине соответствуют 2 множества 2*i и 2*i+1. В множестве 2*i лежат вершины которые точно находятся в одном множестве с i, а в множестве i*2+1 те, которые точно не в одном множестве с i. Таким образом утверждается что запрос "вершины a и b лежат в одном множестве" сводится к unite(a*2, b*2) и unite(a*2+1, b*2+1). А запрос "вершины a и b не лежат в одном множестве" сводится к unite(a*2, b*2+1) и unite(a*2+1, b*2). Утверждается, что при этих операциях свойство про то, что в множестве 2*i лежат вершины которые точно находятся в одном множестве с i, а в множестве i*2+1 те, которые точно не в одном множестве с i, будет соблюдаться, т.к. несложно заметить, что i*2 и i*2+1 всегда имеют одинаковые ранги. Вот исходный код решения: http://pastebin.com/6z1dvy2Q. Что вы думаете по этому поводу? Существуют ли альтернативные алгоритмы? Можно ли решить задачу быстрее(обязательно онлайн)?

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

»
10 лет назад, # |
  Проголосовать: нравится -8 Проголосовать: не нравится

Можно избавиться от второго СНМа, но зато добавить для каждой вершины параметр, который будет означать следующее: находится ли эта вершина в той же доле, что и ее предок в дереве непересекающихся множеств. Дальше додумывай самостоятельно :).

А насчет "быстрее" — вроде бы нельзя. Во всяком случае, если к перечисленным тобой запросам добавить запрос "отменить последнее добавление" — тогда точно нельзя, потому что к такой задаче тривиально сводится за линейное время задача о поддержании системы непересекающихся множеств. А для СНМ была доказана нижняя граница Ω(N * α(N)).

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

    А зачем избавляться от второго СНМа? Так код получается простой и краткий. И преимущество в производительности там вряд ли будет. А разве без отмены последнего действия не сводится?

    • »
      »
      »
      10 лет назад, # ^ |
        Проголосовать: нравится -8 Проголосовать: не нравится

      В-принципе, незачем, это я просто рассказал еще одну идею решения задачи.

      А как свести без отмены последнего действия? Я, например, не понимаю, как без нее отвечать на запрос SameSet(v1, v2). Кстати, даже так получается не совсем полноценное сведение — мы не можем реализовать запрос FindSet(v).

      • »
        »
        »
        »
        10 лет назад, # ^ |
          Проголосовать: нравится -7 Проголосовать: не нравится

        Unite(a, b) будет dsu_unite_same(a, b). А find(a) будет dsu_find(a*2)/2.