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

Автор saprykin, 3 года назад, По-русски

Привет, Codeforces!

В этом посте я бы хотел поделиться тем, как я пишу Дерево отрезков, объясню, почему я это делаю классом, а так же какие у этого плюсы и минусы.
Чтобы не быть голословным я бы хотел рассмотреть свое решение на конкретной задаче (осторожно, идеи к решению)

Код
Решение

Что в этом написании особенного

Основная моя идея в том, чтобы реализовать ДО как класс

Это позволяет использовать для двух деревьев отрезков более понятный интерфейс sgb.upd(l2, r2 + 1) sga.upd(l1, r1 + 1) sga.get(l1, r1 + 1) + sgb.get(l2, r2 + 1) вместо upd1(l1, r1 +1) upd2(l2, r2 + 1) и get1(l1, r1 + 1) + get2(l2, r2 + 1) А если количество деревьев переменно, то по другому это вроде не закодить(по крайней мере я не умею).

Другая не менее важная идея состоит в том, чтобы реализовать две функции каждого типа например get(int v, int l, int r, int L, int R) и get(int L, int R)

Я хочу получить значение на отрезке и не хочу думать о параметрах которые не влияют на запрос. Поэтому get(int v, int l, int r, int L, int R) является внутренним интерфейсом, который используется только для подсчета результата и не вызывается снаружи.

Так же я использую структуру, которая поддерживает приватные и публичные поля.

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

Почему я пишу именно так

Однажды, когда я писал отбор в сириус, была задача в которой n исходная и n ДО не совпадали. И в функции запросов и обновления я писал неверное значение n. За время отбора я так и не раздебагал это. Как итог мне не хватило совсем чуть чуть баллов до прохода. После этой грустной истории я разработал для себя некий стандарт, которому стараюсь соответствовать.

Заключение

Плюсы

  1. Помогает не думать над тем, какой параметр передавать в запрос (Например в приведенной выше задаче, в одном случае размер равен n/2 + 1 а в другом n/2. Подобная ошибка в пару символов может стоить нескольких минут, а в худшем случае и десятков).
  2. Эстетично приятно.
  3. Пишется шаблонно (сначала mrg, потом build, дальше push и тд).
  4. Легко заменить. Нужно только изменить функцию mrg и поля у вершины.
  5. Подходит для задач, где нужно одинаковое ДО для нескольких массивов

Минусы

  1. Дольше работает. Во-первых, как известно статмасивы всегда лучше векторов по скорости, однако создать класс со статмасивом лично у меня не получилось (код крашается). Во-вторых вызов функции из класса вроде работает дольше, чем обычной функции
  2. Дольше пишется.

Вывод

Объективно минусы существенные по сравнению с плюсами. Однако я считаю, что мой способ написания имеет место быть. Он помогает избежать ошибок при написании и использовании не очень внимательным людям (к которым я, конечно же, отношусь). Если решение получает TL заменить vec t; на pii t[MAXN] не составит большого труда. А функции в основном решении уже стоят где надо и с правильными параметрами.

Я никого не призываю начинать писать код по другому. Лишь хочу поделиться своими знаниями. Спасибо за внимание, и высокого рейтинга!

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

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

Если писать ДО снизу то такой проблемы не будет

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

    Какой "такой"? Если тебе надо будет использовать несколько до, то все равно придется писать класс, разве нет?

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

Почему ты не пишешь отдельный класс для вершины в ДО?

»
3 года назад, # |
Rev. 3   Проголосовать: нравится 0 Проголосовать: не нравится

можно же писать так:

int get(int l, int r, int x = 1, int lx = 0, int rx = sz) {
    if (l >= rx || lx >= r) return 0;
    if (l <= lx && rx <= r) return t[x];
    int m = (lx + rx) >> 1;
    return get(l, r, x << 1, lx, m) + get(l, r, x << 1 | 1, m, rx);
}

ну конечно если нужно только одно ДО

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

    А чему sz равно? Если это не константа, то не получится

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

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

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

        ну я пишу как на кф еду, sz = 1 << (ceil(log(n) / log(2))

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

          Я посмотрел твою посылку 125408125. Такое не работает в классах, к сожалению.

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

Поздравляю. Осталось переделать на любой тип, научится передавать merge и подобное, и избавиться от 4 * n, и будет дефолт.

P. S. ...статмасивы всегда лучше векторов по скорости — откуда инфа?

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

    Опыт + информация от других. Могу привести код в котором переписывание вектора на статмасив ускорило почти в 2 раза код(но речь не про до совсем)