Быстрый ввод-вывод в C++
Часто бывает так, что ввод и/или вывод в задаче очень большие. В этом случае только считывание входных данных и вывод ответа уже могут не уложиться в ограничение по времени. Чтобы этого избежать, есть различные техники, которые мы рассмотрим в этой главе.
endl и \n
Обычно перевод строки делается при помощи cout << endl
, однако эта команда не только переводит строку, но и очищает буфер. Когда вы что-то выводите, на самом деле оно не обязано выводиться сразу. Программа сначала накопит какой-то буфер вывода, а потом выведет его целиком, потому что сама по себе операция вывода весьма долго работает. Однако если вы пишете endl
, то вы заставляете программу насильно очистить буфер. Если вы будете делать это очень часто, то программа будет работать очень долго. Чтобы решить эту проблему, можно вместо endl
использовать cout << '\n'
. Отличие заключается как раз в том, что это просто символ перевода строки без очистки буфера. В случае, если вы в выводе часто делаете перевод строки, это может существенно ускорить вашу программу.
Однако не стоит везде всегда писать \n
вместо endl
. В интерактивных задачах важно, чтобы после каждого вывода очищался буфер, чтобы программа, с которой вы взаимодействовала, могла сразу считать то, что вы вывели. Поэтому в интерактивных задачах обязательно использовать endl
для очистки буфера.
Кроме того, если вы пытаетесь найти ошибку в программе и выводите отладочную информацию, вам важно, чтобы она появлялась на экране, поэтому ее тоже нужно выводить с очисткой буфера. Если вы не хотите при этом переводить строку, можете воспользоваться std::flush
.
sync_with_stdio и cin.tie
Также сильно ускорить программу можно, отключив синхронизацию разных потоков ввода/вывода. Можно использовать следующие две строчки:
ios::sync_with_stdio(0);
cin.tie(0);
Первая строка отключает синхронизацию iostream
и stdio
, то есть вы не можете больше одновременно использовать и cin
, и scanf
в одной программе. Однако вряд ли вам это нужно, поэтому эту синхронизацию можно отключить, что приводит к ускорению программы.
Вторая строка отключает привязку cin
к cout
, что помогает в случае, когда ввод и вывод чередуются. Кроме того, некоторые люди иногда используют cout.tie(0)
, но эта строка не делает ничего, потому что пытается отключить привязку cout
к cout
, что весьма странно. Миф о том, что cout.tie
полезен подкрепляется примерами, когда при добавлении этой строки код ускорялся, однако при повторных запусках могла наблюдаться обратная ситуация. Разное время работы связано с разными условиями запуска и фазами Венеры, но не с добавлением этой строчки.
Ручной быстрый ввод-вывод
Если даже предыдущие оптимизации не помогают, можно воспользоваться рукописным быстрым вводом. Это будет еще в несколько раз быстрее. К примеру, можно воспользоваться вот этим кодом от Сергея Копелиовича. Пример использования:
int n = readInt();
writeInt(n, ' ');