Why this matters when you build with AI
A model that writes competitive code often reads input in the simplest way: one cin >> x or one input() per number. That works for a sample with five numbers. On a file with a million numbers, the same program can take far longer than the judge allows, while the algorithm itself is correct. The verdict then says time limit exceeded on a large test, and the sample still passes.
The fix is almost never a new algorithm. It is a change in how the program reads and writes. This lesson shows the changes, measures them, and gives you a way to check code an AI tool wrote for your input and output.
Be careful with AI-written I/O code. A model may produce a fast reader that forgets the end of the input, or an interactive program that never flushes its queries. Both look fine on a sample. Check the input size in the constraints, time the program on a large generated file, and read the flush rules of the problem before you trust the code.
Words used in this lesson
Input and output, shortened to I/O, means reading data into the program and writing results out. A stream is C++'s object for reading or writing, such as cin for input and cout for output. A buffer is a block of memory that holds bytes between the program and the device, such as a file or a keyboard. Reading from a buffer that is already full is cheap. Asking the operating system for more bytes is expensive, and that cost is what buffers hide.
A token is one piece of input separated by spaces or line breaks, such as one number. Flush means sending the bytes in the output buffer out now, instead of waiting until the buffer is full.
TLE means time limit exceeded: the program ran longer than the limit of the test. An interactive problem is one where your program and the judge talk to each other. Your program prints a question, the judge answers, and so on. Flushing is required there.
Why slow reading causes TLE
Consider a file with a million numbers, one per line. A naive program calls the input function a million times. Each call has fixed overhead, such as checking the state of the stream, locking, and converting text to a number. A few hundred nanoseconds per call adds up to a fraction of a second. In Python, each call costs much more, so the same work can take seconds.
The fix has two parts. First, make the program read a large chunk of bytes in one go. Second, parse the numbers from memory, which is fast. The time spent in the operating system stays the same, but the per-number overhead falls.
Here are the timings measured for this lesson. The input was one file of 1,000,000 integers, one per line, values from 0 to 10^9. Each program ran five times, and the table shows the median wall-clock time. The machine was a WSL2 Linux environment, g++ 15.2 with -O2, Python 3.14.4. Your numbers will differ, but the order should be similar.
| Program | Median time |
|---|---|
C++ cin with default settings | 0.54 s |
C++ cin with sync off and untied | 0.09 s |
C++ scanf | 0.13 s |
| C++ custom buffered reader | 0.03 s |
Python input() for each line | 1.70 s |
Python sys.stdin.readline for each line | 0.33 s |
Python sys.stdin.buffer.read().split() | 0.32 s |
The default C++ cin took about half a second on this machine, so it may pass a judge with a generous limit. The point is the gap: by median time, the buffered reader is about 16 times faster than default cin here, and the Python input() loop is about five times slower than the buffered Python version. Check the time limit of your own problem before you decide how far to go.
Technique 1: C++ streams in fast mode
Two lines change how cin and cout behave.
ios::sync_with_stdio(false);
cin.tie(nullptr);sync_with_stdio(false) tells C++ streams to stop synchronising with the C input and output functions, scanf and printf. By default, each cin read goes through the C library's buffer, which costs time. With the sync off, the stream keeps its own buffer.
The price is that you must not mix the two families after this line. The next program writes with cout and printf alternately. Its output order changes, as shown below.
// Mixing cout and printf after sync_with_stdio(false) can change the output order.
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cout << "from cout 1\n";
printf("from printf 1\n");
cout << "from cout 2\n";
printf("from printf 2\n");
cout << "from cout 3\n";
}Output when stdout is a pipe or a file, from g++ 15.2 -O2 on this machine:
from cout 1
from cout 2
from cout 3
from printf 1
from printf 2The same program without the sync line prints the lines in program order. Pick one family and stick to it.
cin.tie(nullptr) removes the link that makes cin flush cout before it waits for input. By default, when you read with cin, any text waiting in cout is flushed first. That default is helpful in an interactive problem, and it costs time in a batch problem. Untying is safe for batch input. For interactive input, you must flush cout yourself, as explained later in this lesson.
With both lines in place, the default cin is replaced by a faster stream, and the 0.54 s from the table becomes about 0.09 s on this machine.
Technique 2: scanf and printf
scanf and printf are the C functions for formatted input and output. They are fast and they do not need the sync line. Check the return value of scanf. It is the number of values read, and a program that ignores it does not know when the input ends. The compiler warns about this with the message ignoring return value of 'int scanf(...)' declared with attribute 'warn_unused_result'.
if (scanf("%lld", &x) != 1) break;Use %lld for long long. A mismatch between the format and the type is undefined behaviour, which means the program may print anything or crash.
Technique 3: a custom buffered reader
A buffered reader asks the system for a block of bytes, for example 64 KiB, and then reads the numbers from that block one byte at a time. When the block is used up, it asks for the next block. The fread function is standard C and works on every platform.
static char buf[1 << 16];
static size_t buf_len = 0, buf_pos = 0;
static inline int read_byte() {
if (buf_pos == buf_len) {
buf_len = fread(buf, 1, sizeof(buf), stdin);
buf_pos = 0;
if (buf_len == 0) return -1; // end of input
}
return static_cast<unsigned char>(buf[buf_pos++]);
}
// Reads one signed integer. Returns false at end of input.
static inline bool read_ll(long long &out) {
int c = read_byte();
while (c != -1 && c != '-' && (c < '0' || c > '9')) c = read_byte();
if (c == -1) return false;
bool neg = false;
if (c == '-') {
neg = true;
c = read_byte();
}
long long x = 0;
while (c >= '0' && c <= '9') {
x = x * 10 + (c - '0');
c = read_byte();
}
out = neg ? -x : x;
return true;
}Three details matter. The function skips any bytes that are not digits or a minus sign, so spaces, tabs, carriage returns and extra blank lines are all handled. It returns false at the end of the input, so the caller can stop. And it returns static_cast<unsigned char>, so a byte value is never negative.
Some guides use getchar_unlocked instead of fread. It is faster in some cases, but it is not part of standard C++ and may not exist on every system. fread avoids that question.
Python: reading and writing fast enough
In Python, the cost of a call is high, so the rule is to make few calls. Three options were measured above.
input() is the slowest choice, and it is the one most beginners write. Use it for small inputs only.
sys.stdin.readline is a clear improvement. It reads one line per call, so it still makes one call for each number when the numbers are on separate lines.
sys.stdin.buffer.read().split() reads the whole input in one call and splits it into tokens. The tokens are bytes objects, and int() converts them directly, since Python 3 accepts bytes there. This is the fastest of the three and is a good default for competitive code.
import sys
data = sys.stdin.buffer.read().split()
n = int(data[0])
total = 0
for tok in data[1:n + 1]:
total += int(tok)
print(total)For output, build the lines in a list and write them with one call, for example sys.stdout.write("\n".join(map(str, result)) + "\n"). Writing one line per print call on a large output is slow for the same reason as slow input.
Output: flush only when you need to
endl in C++ writes a newline and then flushes the stream. In a loop that prints a million lines, that is a million flushes. On this machine, printing the numbers 1 to 1,000,000 to a file took a median of 1.38 s with endl, and 0.065 s with '\n'. The output bytes were identical, as the checksum showed. Use '\n', and flush only when the problem requires it.
Writing the whole output as one string and calling fwrite once was as fast as '\n'. It is useful when you already build the output in a string for another reason.
Flush rules for interactive problems
In an interactive problem, your program and the judge talk through pipes. Your program prints a question, such as ? 37, and waits for the answer. The judge waits for your question before it answers. If the question is still in your program's output buffer, neither side moves, and the judge stops waiting after its time limit.
The rule is simple. After every question, flush the output before you read the answer.
cout << "? " << mid << '\n';
cout.flush();cout << endl also works, because endl flushes.
The default C++ settings are kinder than the fast mode above. With cin tied to cout, reading with cin flushes cout first, so a program that forgets to flush still works. Once you untie with cin.tie(nullptr), the flush is your job.
In Python, print(..., flush=True) flushes standard output right after that line. sys.stdout.flush() flushes everything written so far. input() flushed standard output before waiting in the Python version tested here, but sys.stdin.readline did not. Do not depend on that behaviour. Flush explicitly.
Each interactive problem has its own protocol, such as the number of queries allowed and the line to print at the end. Read it first.
To test the rules, a toy judge was written for this lesson. The hidden number is between 1 and 100. The program may send ? x, and the judge answers <, > or =. After =, the program prints ! H. The judge waits up to two seconds for each line from the program. The judge uses Unix select on pipes, so it runs on Unix-like systems such as Linux and macOS, not on Windows.
| Solution | Flushes its query | Result with hidden value 37 |
|---|---|---|
C++, cin.tie(nullptr), explicit cout.flush() | yes | answer 37 after 3 queries, correct |
| C++, default tie, no explicit flush | no, cin flushes | answer 37 after 3 queries, correct |
C++, cin.tie(nullptr), no flush | no | TIMEOUT after 0 queries |
Python, print(..., flush=True) and sys.stdin.readline | yes | answer 37 after 3 queries, correct |
Python, print with no flush and sys.stdin.readline | no | TIMEOUT after 0 queries |
Python, print with no flush and input() | no, input() flushed here | answer 37 after 3 queries, correct |
The four solutions that passed were also run for all 100 hidden values, and each one answered correctly for every value. The toy judge is a teaching tool. A real judge has its own rules, and its own limit on queries.
Worked example: the Step Counter
Here is an original problem. Read it fully before you look at the solution.
Step Counter. The first line has an integer n, from 1 to 10^6. The next n tokens are integers a_1 to a_n, with absolute value at most 10^9. The tokens may be on any lines. Print two numbers separated by a space: the sum of all the a_i, and the number of indices i from 2 to n with a_i greater than a_(i-1).
The sample input is six numbers, and the answer is 20 and 2.
6
3
5
2
2
9
-1The solution uses the buffered reader from technique 3 with a small main. It reads each number once, keeps the previous number, and counts the rises.
// Step Counter. Input: n, then n integers a_1..a_n on any whitespace-separated lines.
// Output: the sum of all a_i, and the number of indices i >= 2 with a_i > a_{i-1}.
#include <bits/stdc++.h>
using namespace std;
static char in_buf[1 << 16];
static size_t in_len = 0, in_pos = 0;
static inline int read_byte() {
if (in_pos == in_len) {
in_len = fread(in_buf, 1, sizeof(in_buf), stdin);
in_pos = 0;
if (in_len == 0) return -1;
}
return static_cast<unsigned char>(in_buf[in_pos++]);
}
static inline bool read_ll(long long &out) {
int c = read_byte();
while (c != -1 && c != '-' && (c < '0' || c > '9')) c = read_byte();
if (c == -1) return false;
bool neg = false;
if (c == '-') {
neg = true;
c = read_byte();
}
long long x = 0;
while (c >= '0' && c <= '9') {
x = x * 10 + (c - '0');
c = read_byte();
}
out = neg ? -x : x;
return true;
}
int main() {
long long n = 0;
if (!read_ll(n)) return 0;
long long sum = 0, ups = 0, prev = 0;
for (long long i = 0; i < n; i++) {
long long a = 0;
if (!read_ll(a)) break;
sum += a;
if (i > 0 && a > prev) ups++;
prev = a;
}
printf("%lld %lld\n", sum, ups);
return 0;
}Compile with g++ -std=c++17 -O2 -Wall -Wextra -o step_counter step_counter.cpp. There are no warnings. Run it on the sample.
20 2Check by hand. The sum is 3 + 5 + 2 + 2 + 9 - 1 = 20. The rises are 5 after 3, and 9 after 2, so the count is 2. The 2 after 5 is a fall, and the 2 after 2 is equal, so neither counts.
A brute force for this problem is a plain Python version that reads with input() and compares neighbours in a loop.
# Brute force: compares every pair of neighbours with a plain loop and input() for every line.
n = int(input())
a = [int(input()) for _ in range(n)]
total = sum(a)
ups = 0
for i in range(1, n):
if a[i] > a[i - 1]:
ups += 1
print(total, ups)The C++ solution, the brute force, and a fast Python version that uses sys.stdin.buffer.read().split() were run on 300 random inputs, each with up to 15 numbers from -20 to 20, one number per line and an optional final newline. All three printed the same answer on every case, and the test script also computed the expected answer directly. Those tests check the logic on small inputs. The timing of the C++ solution on a file of 1,000,000 numbers was 0.01 s in this environment, measured with a single run.
Complexity. The solution reads each number once and does constant work for each one, so the time is O(n). It keeps only a few variables and the 64 KiB buffer, so the extra memory does not grow with n.
Hands-on exercise: measure the gap on your machine
This exercise takes about twenty minutes. You will generate a large input, time several readers, and record the gap in a table in your own notes.
Save this generator as gen_numbers.py. It writes n, then n lines with one integer each.
# Writes a test file: first line n, then n lines with one integer each (0 to 10^9).
import random, sys
n = int(sys.argv[1]); seed = int(sys.argv[2]); out = sys.argv[3]
rng = random.Random(seed)
with open(out, "w") as f:
f.write(f"{n}\n")
f.write("\n".join(str(rng.randint(0, 10**9)) for _ in range(n)))
f.write("\n")Run python3 -I gen_numbers.py 1000000 2026 numbers.txt. The -I flag runs Python in isolated mode, so it does not load modules from the current folder.
Save two programs. The first is naive_cin.cpp, which reads with default cin and has no sync line. It computes the same two numbers as the second, which is the reader from technique 3 with this main.
// Naive: C++ streams with the default settings (synced with C stdio, tied to cout).
#include <bits/stdc++.h>
using namespace std;
int main() {
long long n;
cin >> n;
long long sum = 0, x_or = 0;
for (long long i = 0; i < n; i++) {
long long x;
cin >> x;
sum += x;
x_or ^= x;
}
printf("%lld %lld\n", sum, x_or);
}int main() {
long long n = 0;
if (!read_ll(n)) return 0;
long long sum = 0, x_or = 0;
for (long long i = 0; i < n; i++) {
long long x = 0;
if (!read_ll(x)) break;
sum += x;
x_or ^= x;
}
printf("%lld %lld\n", sum, x_or);
}Compile both with -std=c++17 -O2.
Time each program on numbers.txt three times with your shell's time command, for example time ./buffered_reader < numbers.txt. Record the real time of each run. Check that the two programs print the same two numbers.
Your table should have one row per program, the three run times, and the ratio between the naive program and the buffered one. Write one sentence on whether the gap matters for the time limit of a problem you have seen.
Show the answer
On the machine used for this lesson, the naive cin program took a median of 0.54 seconds and the buffered reader a median of 0.03 seconds, a ratio of about 16 times. Your numbers will differ with your processor, operating system and disk. The result should still show the buffered reader far ahead. The two programs must print the same sum and xor. If they do not, check the reader for a missing - sign or a missing end-of-input check.
Common mistakes
Mixing printf and cout after the sync line. Symptom: the lines appear in a different order from the program. The example in technique 1 printed from cout 1, from cout 2, from cout 3, then the printf lines. The fix is to use one family only, or to remove the sync line.
Using endl in a loop. Symptom: the program is correct and slow. Printing one million lines with endl took 1.38 s on this machine, against 0.065 s with '\n'. The fix is '\n'.
Forgetting the flush in an interactive problem. Symptom: the program hangs at its first question, and the judge reports that it did not receive the next line in time. The toy judge gave TIMEOUT after 0 queries for the untied program without a flush. The fix is cout.flush() after each question, or print(..., flush=True) in Python.
Reading without checking for the end of input. A reader that ignores the return value of scanf continues with the old value. The g++ warning for a missing initial value reads 'n' may be used uninitialized [-Wmaybe-uninitialized]. Give every variable a starting value, and check scanf or your reader's return value before using the number.
Using input() for a large file in Python. Symptom: the sample passes, and a test with a million numbers exceeds the limit. In this lesson, input() took 1.70 s on the same input where sys.stdin.buffer.read().split() took 0.32 s.
Reading bytes as characters. In Python, sys.stdin.buffer.read() gives bytes, and the tokens are bytes objects. Comparing a token to the string "OK" fails silently, because b"OK" != "OK". Decode the tokens, or compare with b"OK".
Checklist
- Check the input size in the constraints before you choose a reader.
- For C++ batch input, use
ios::sync_with_stdio(false)andcin.tie(nullptr), or a buffered reader, and do not mix stream families. - Print with
'\n', notendl, unless an interactive protocol needs a flush. - Check every return value that reports success or end of input.
- For interactive problems, flush after every question, and read the protocol.
- In Python, read the whole input with
sys.stdin.buffer.read().split()for large inputs. - Time the program on a generated file of the largest size before you submit.
Practice
Work these on paper or in a file before you look at the answers.
Practice 1. Find the slow lines. A program prints 200,000 lines with cout << value << endl; and reads 200,000 numbers with cin >> x, without any sync line. Name the two changes that remove most of the per-line cost, and write the two lines of code that make them.
Show the answer
Hint: one change is on output, one on input, and both are in the first lines of main. Answer: write ios::sync_with_stdio(false); cin.tie(nullptr); at the top of main, and change endl to '\n'. Do not mix in printf after the sync line.
Practice 2. An interactive program that hangs. Predict what the toy judge prints for this solution with hidden value 37, and say what to add to fix it.
#include <bits/stdc++.h>
using namespace std;
int main() {
cin.tie(nullptr);
int lo = 1, hi = 100;
while (lo <= hi) {
int mid = (lo + hi) / 2;
cout << "? " << mid << '\n';
string reply;
cin >> reply;
if (reply == "=") { cout << "! " << mid << '\n'; return 0; }
if (reply == "<") hi = mid - 1; else lo = mid + 1;
}
return 0;
}Show the answer
Hint: the first question stays in the buffer, because cin is untied and nothing flushes cout. Answer: the toy judge prints TIMEOUT after 0 queries: the solution did not send its next line. Add cout.flush(); after the line that prints the question. You can also remove cin.tie(nullptr);, and then the default tie flushes the question before the read.
Practice 3. Rewrite a slow reader. Rewrite this Python program so it reads all the input in one call. It must print the same output for any valid input.
n = int(input())
total = 0
for _ in range(n):
total += int(input())
print(total)Show the answer
Hint: read everything with sys.stdin.buffer.read().split(), then take the first token as n and convert the next n tokens. Answer:
import sys
data = sys.stdin.buffer.read().split()
n = int(data[0])
total = 0
for tok in data[1:n + 1]:
total += int(tok)
print(total)For any valid input, both versions print the same sum. The first version reads the whole input at once, so it does not make one call per line.
Practice 4. Predict the output order. A program calls ios::sync_with_stdio(false);, then prints "A" with cout, "B" with printf, and "C" with cout, each followed by a newline. Write the order the lines appear when stdout is a pipe. Then say how to get program order.
Show the answer
Hint: the unsynced cout lines sit in the stream's own buffer, while printf writes through the C buffer, so the two families flush at different times. Answer: when stdout is a pipe or a file, the order was A, C, B (run with g++ 15.2 -O2 on this machine). To get program order, remove the sync line, or use printf for all output.
Practice 5. Empty input. Run the Step Counter program on an empty file, and say what it prints and why it is safe. Then say what would change if read_ll(n) were not checked.
Show the answer
Hint: read_ll returns false at the end of the input. Answer: the program prints nothing and exits with status 0, which was checked with /dev/null as input. The check stops the program before the loop. Without it, the program would not know that the read failed, because the return value is the only signal. Giving n a starting value of 0 keeps the variable defined either way.
Summary of the tools
Slow I/O is a per-token cost. C++ streams become fast with the sync line and the untie line, scanf and printf are fast and simple, and a custom buffered reader is the fastest C++ option in this lesson. Python reads fastest when it reads the whole input once. Output with endl flushes too often, so use '\n'. Interactive problems need an explicit flush after each question, and the flush rules change with the stream settings.