Ответы пользователя по тегу C++
  • Как работать с большими числами в C++?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Если числа такие большие, что ответ никуда не влезает, то надо писать длинную арифметику. Цифры числа храните в массиве, складывайте/умножайте в столбик. Удобно хранить числа развернутыми. Гуглите "длинная арифметика" - найдете и код с примерами и кучу статей.

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

    Ваш код так ужасен, что я не могу понять, что вам там надо подсчитать в итоге, но уже брасается в глаза, что у вас там считается число сочетаний. Во-первых, можно считать (n-k+1*k+2*..*n)/k!, что уже дает более мелкие числа.

    Потом, можно считать треугольником паскаля. Можно даже считать только одну строку, домножая и деля на одно число - тоже не получая в процессе чисел больше ответа.

    Если bills не больше 64, то все влезет в стандартный unsigned long long.

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

    Если в задаче более сложная формула, то можно хранить числа в виде их факторизации на простые числа. Поддерживайте массив в котором будете хранить степени всех простых чисел до некоторого максимального. При умножении прибавляйте, при делении вычитайте. В конце все оставшееся перемножте.

    Еще более продвинутый метод: считать по модулю больших простых чисел и в конце, через китайскую теорему об остатках вычислять ответ.
    Ответ написан
    Комментировать
  • Вылетает программа на C++ с кодом -1073741571 (0xC00000FD)?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Попробуйте пройдтись дебаггером, или отладочный вывод везде добавить. В каждую функцию, в каждый цикл. Тогда станет понятнее, где оно падает.

    А так вижу несколько ошибок. Обычно дерево отрезков реализуют только на массиве длинной в степень двойки. Добейте a нулями, до степени двойки. И тогда массив t может быть только в 2 раза больше a, а не в 4.

    Далее, при реализации get надо, если отрезок вершины лежит внутри отрезка запроса, возвращать t[v]. в противном случае надо запускаться слева и справа, но тольо если запрос торчит в нужную сторону. Вы не запускаетесь от левого сына, если l > m, например. Можно считать значение 0, если рекурсивного вызова нет. В конце берете gcd двух половинок.
    Ответ написан
  • Как правильно передать двойной массив из класса наследника C++?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Проблема в том, что Box::box - член класса и он инициализируется в конструкторе и в тот самый момент, где вы пытаетесь его использовать в конструкторе предка, оно оказывается еще не инициализировано.

    Варианты решения:
    - вынести эту костанту куда-то, например сделать ее глобальной.
    - сделать Box::box static const (или constexpr - инче сами числа надо будет вынести вне объявления класса)
    - использовать Box::box в теле конструктора. Но тогда надо сделать конструктор по умолчанию для Figure и вызывать fill_figure в конструкторе Box.
    - создавать значение массива прямо в вызове конструктора Figure из Box:
    Box() : Figure( (const bool[4][4]){
          {0, 0, 0, 0},
          {0, 1, 1, 0},
          {0, 1, 1, 0},
          {0, 0, 0, 0}
      }, "Box") {};


    Лучше, конечно, завести static const.
    Ответ написан
  • Что происходит с string при передаче ссылки строки в структуру, почему может крашится?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    brick = (OBJECT*)realloc(brick, sizeof(*brick) * brickLength);


    Вот этот код вызывает ошибку. Вы выделяете стурктуру OBJECT через realloc. Но она не POD (plain old data) - там поле url - объект std::string. А объекты нельзя вот так выделять. Надо чтобы обязательно конструктор отработал. Вообще, конечно, есть способ извратиться и вызвать конструктор руками, но это костыль.

    Вы тут намешали вещи из C++ (объекты) и вещи из C (malloc) - и это все вместе не работает.

    Правильное решение будет создавть объект через new:
    brick = new Object[brickLength];

    Не забудьте только в конце отчистить это все через delete[].

    А еще лучше, используйте std::vector<Object>.
    Ответ написан
    1 комментарий
  • Как разрешить использование только конкретного наследуемого от интерфейса, не финального класса?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Практически никак. Суть полиморфизма в том, что экземпляры наследников можно вставлять вместо экземпляра предка везде и все будет работать. Поэтому вы средствами языка никак не запретите передавать C вместо B (речь об указателях, естественно). Если хочется, то можно сделать виртуальную функцию, которая возвращала бы какое-то enum, для идентификации классов A,B,C (и других наследников) и уже в функции проверять, что там не передали по ошибке экземпляр C. Но логичнее было бы выделить каие-то свойства у классов (например, количество пуль в вашем примере с MultiGun) их задать в виртуальных функциях у всех классов и дальше в функции проверять, что переданный экземпляр обладает нужными свойствами. Жестко привязываться, что можно B, но нельзя C - это плохой подход. Почему нельзя? А если потом появится D,E,F - можно ли их передавать? А если они братья B?
    Ответ написан
    Комментировать
  • Имеется ли в C++ данный синтаксис?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Есть. Это называется aggregate initializers. Доступно с C++20.

    Ну и, первая строка - это какой-то макрос, который, судя по контексту, разворачивается в декларацию переменной какого-то аггрегатного типа.
    Ответ написан
    1 комментарий
  • Как принять число любой длины?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Вам не только принимать числа любой длины в конструкторе, вам их хранить и обрабатывать придется. Чтобы работать с данными любой длины придумали массивы. Например, передавайте строку или std::vector.
    Ответ написан
    Комментировать
  • В чем может быть проблема не считывания с файла?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Смотрите внимательно, что программа выводит. Где там пробел выводится-то?
    Ответ написан
    Комментировать
  • Как упростить код?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Ну вот ваше последнее описание и есть решение - храните 2 стека. Вы же храните 1 стек и лишь в определенном случае заводите второй стек tmp.
    Ответ написан
    Комментировать
  • Как извлечь элементы многобайтового массива как единое число?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Лучше так делать не надо. Это UB - нарушение всяких strict aliasing, выравниваия и вообще от порядка байт в машине зависит. Лучше руками собрать ULL по частям, вроде
    for (int i = 0; i < 8; ++i) result |= byte_array[i+1] << (8ULL*i);
    или
    for (int i = 0; i < 8; ++i) result |= byte_array[i+1] << (8ULL*(7-i));


    На худой конец, если очень узкое место, надо делать memcpy из массива в &result.
    Ответ написан
    Комментировать
  • Почему явная специализация невозможна?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Проблема вызвана использованием шаблона rewrite из шаблона search.

    Если вы перенесете специализацию шаблона rewrite вверх, до специализации search, то все скомпилируется. Или надо где-то выше первого использования шаблона rewrite задекларировать специализацию (что ваш закомментированный код и делает).

    Вызвана эта ошибка стандартом.
    Надо, чтобы специализация шаблона была задекларирована до любого использования:
    Specialization must be declared before the first use that would cause implicit instantiation, in every translation unit where such use occurs:
    Ответ написан
    1 комментарий
  • Уменьшается ли используемая память программы?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Не гарантированно, но в некоторых случаев компилятор действительно сможет переиспользовать место на стеке под переменную a для какой-то новой локальной переменной, когда a выйдет из зоны видимости. Но чаще это место просто будет пустым до конца функции и никакой экономии памяти вы не получите.

    Но вообще, делать так для экономии памяти никогда, категорически не рекомендуется. Код становится менее читаем а экономите вы на спичках. Это локальные переменные - они на стеке. Их много можно выделить только рекурсией или большими массивами (ну не объявите вы в коде миллион локальных переменных). В обоих случаях, если стека не хватает - надо или избавлятся от рекурсии/больших массивов изменением логики, или выносить их в кучу.

    Использование фигурных скобок для ограничения зоны видимости переменной действительно используется на практике, когда вам надо ограничить время жизни переменной и добиться вызова деструктора в определенное время. Так делают, например, когда захватывают мютекс в многопоточных программах - специальный класс-обертка в конструкторе его хватает, в деструкторе освобождает. И иногда не надо держать мютекс во всей функции, а только в определенном месте. Допустим, дальше идут долгие вычисления, не требующие мютекса. Тут логично мютекс освободить. Но это должно встречаться редко. Если вам в функции надо несколько раз такое проворачивать, то надо ее отрефакторить и разбить на части.
    Ответ написан
    Комментировать
  • В чём отличия языка Си и С++? Зачем нужен Си?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    По учебнику, фундаментальных отличий три: классы, шаблоны и исключения. По факту там еще целый ворох мелких различий и это два вообще разных языка, хоть и слегка похожих.

    C сейчас используется в основном там, где легаси (куча старого кода в проекте уже на C), или где очень жесткие требования к легкости окружения исполнения языка (микроконтроллеры всякие).
    Ответ написан
    2 комментария
  • Корректно ли в C++ называть стек статической памятью?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Стек стоит рассматривать как отдельную категорию. И статической памятью его называть точно не стоит.
    Хотя бы потому, что для работы с ним есть специальные команды процессора.
    Плюс, он хоть и выделяется программе при загрузке, в отличии от статических данных, обращаться к данным в нем можно не всегда - а только ниже по стеку вызовов.
    Ответ написан
    Комментировать
  • Скажите как можно исправить тайм лимит в коде?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Сумму на отрезке надо делать хитро: в каждой вершине поддерживайте счетчик, сумму значений всех вершин в поддереве, а так же минимальное и максимальное значение в поддереве.

    Рекурсивно спускайтесь, если текущее поддерево целиком лежит в запросе - возвращайте известную сумму.
    Иначе запускайтесь слева или справа только если отрезок там пересекается. Или можно просто возвращать 0 вместо этих проверок, если все вершины в поддереве не попадают в отрезок (меньше левой границы запроса или больше правой). Такой подход найдет сумму за O(log n) а не за O(n).
    Ответ написан
  • Более общее свойства дерева поиска?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Что за last_key? Почему дерево считается неправильным, если самая левая вершина равна numeric_limits<int>::min()

    Эта задача, в отличии от предыдущих двух ваших вопросов - простая. Тут не надо писать балансированное дерево поиска, а надо тупо проверить, что заданное дерево хорошее.

    Отличие от прошлой задачи, упомянутой в вопросе - только в том, что теперь ограничения в каждом поддереве не L < key < R, а L <= key < R. Т.е. отличие от кода в конце вопроса, по идее, должно состоять только в одном знаке <= вместо <

    Есть, правда, дополнительная проблема, что числа могут быть очень большие и, например, надо было бы в самом начале передавать numeric_limits<int>::max()+1 в качестве правой границы. Но тут можно решить эту проблему просто заменив тип параметров max_key, min_key на long long. Или изменить их смысл на "ключи в этом поддереве могут быть с L по R включительно". Тогда при переходе к левому сыну надо бы передавать tree[i].key-1 в качестве max_key. Но тогда надо аккуратно и не переполниться при numeric_limits<int>::min(). Но тут тогда нужна дополнительная проверка - любая вершина со значением numeric_limits<int>::min() не должна иметь левых детей.
    Ответ написан
    Комментировать
  • Что быстрее: создание вектора push_back или сначала объявление сколько в нем переменных, а потом заполнение?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Сначала объявление будет быстрее. Тут всего одно выделение памяти, а при push_back их будет несколько (но не n, потому что vector умный и выделяет памяти с запасом).

    Но push_back не на порядок медленнее. В худшем случае в пару раз. И то и другое будет работать за O(n). Если у вас программа еще хоть что-то делает, кроме запихивания чисел в массив, то вы разницу особо и не заметите.

    Если только профайлер не показывает что вот это вот самое медленное место в программе, то заморачиваться не стоит.
    Ответ написан
    3 комментария
  • Множество с запросами?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Ваше решение медленно считает сумму на отрезке. За O(n) - вы проходитесь по всему множеству. А все ваше рение будет O(n^2). Когда как это надо делать за O(log n) максимум. Ну посмотрите же на ограничения - n <= 10^5. Обычно n^2 работает ну до 10^4 максимум с огромным скрипом и натяжками.

    К сожалению, стандартными set'ами вы сумму на отрезке быстро не подсчитаете. Придется реализовывать сбалансированное бинарное дерево поиска, где в каждой вершине надо дополнительно хранить сумму всех значений в поддереве. Вот реализация дерева, но там сумма на отрезке не реализована. Однако, в статье расказанно, как это делать. Ищите на странице "на отрезке".

    Если бы числа в задаче был поменьше, то можно было бы использовать дерево отрезков. Это сильно проще реализуется. Но тут бы это потребовало массивы на 2*10^9 элементов.

    Edit: вообще, судя по нескольким вопросам от вас, у вас там задачи именно на реализацию бинарных деревьев поиска с дополнительными данными в вершинах. Вот если у вас следующая задача опять не пройдет по времени, то сначала убедитесь, что вы используете собственноручно написанное дерево поиска.
    Ответ написан
  • Почему код не проходит по тайм лимиту?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    С такими ограничениями надо менять алгоритм. У вас наивная реализация за O(nq). А надо реализовывать rope за O(q log n).

    Какими-то стандартными контейнерами это не сделать вообще никак.

    Советую реализовывать декартово дерево по неявному ключу. Это самая простая реализация будет. Ну, а дальше вам надо будет это дерево разрезать на три части двумя Split, две крайние объеденить, разрезать это в новом месте и слить уже 3 части с вырезанным куском в середине.
    Ответ написан
  • Как работает данный алгоритм проверки числа на простоту и какой у него Big O??

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Это вроде как решето Эратосфена (только с багами).

    Этот цикл по идее помечает составными все числа, делящиеся на i (которое к этому моменту вроде как простое). Таким образом все составные числа должны быть помечены в массиве a.
    Тут есть оптимизация: помечаются только те числа, для которых i - делитель не больше корня. Иначе бы цикл был не от i*i а от i. Эту оптимизацию можно делать, потому что у каждого числа обязательно есть простой делитель не больше корня, а значит и такой цикл пометит все составные числа.

    Сложность тут O(n log(log n)) - Доказательство смотрите в википедии.
    Ответ написан
    Комментировать