Skip to main content

Fast I/O & Debugging

Why I/O Speed Matters

In competitive programming, I/O can be the bottleneck. A problem with 10^6 integers using cin/cout without optimization will TLE, while the same code with fast I/O passes easily.
The I/O Bottleneck: Standard cin/cout is synchronized with C’s scanf/printf by default, making it significantly slower. Disable this sync to gain 5-10x speedup.

The CP Template

Every competitive programmer has a template. Here’s a solid starting point:
#include <bits/stdc++.h>
using namespace std;

// Type aliases for faster typing
using ll = long long;
using ld = long double;
using pii = pair<int, int>;
using pll = pair<ll, ll>;
using vi = vector<int>;
using vll = vector<ll>;
using vvi = vector<vi>;
using vpii = vector<pii>;

// Macros for common operations
#define all(x) (x).begin(), (x).end()
#define sz(x) (int)(x).size()
#define pb push_back
#define ff first
#define ss second
#define rep(i, a, b) for (int i = (a); i < (b); i++)
#define per(i, a, b) for (int i = (b) - 1; i >= (a); i--)

// Constants
const int MOD = 1e9 + 7;
const int INF = 1e9;
const ll LINF = 1e18;

void solve() {
    // Your solution here
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int t = 1;
    cin >> t;  // Comment this for single test case
    while (t--) {
        solve();
    }
    
    return 0;
}

Fast I/O Techniques

Pattern 1: Sync Disable (Essential)

int main() {
    // MUST be at the start of main
    ios::sync_with_stdio(false);  // Disable sync with C I/O
    cin.tie(nullptr);             // Untie cin from cout
    
    // Now cin/cout are as fast as scanf/printf
}
Important: After ios::sync_with_stdio(false), do NOT mix cin/cout with scanf/printf in the same program.

Pattern 2: Reading Entire Lines

// Reading a line with spaces
string line;
getline(cin, line);

// After reading a number, clear the newline before getline
int n;
cin >> n;
cin.ignore();  // or getline(cin, line); to consume newline
getline(cin, line);

Pattern 3: Reading Unknown Number of Inputs

// Read until EOF
int x;
while (cin >> x) {
    // Process x
}

// Read n integers into vector
int n;
cin >> n;
vector<int> a(n);
for (int& x : a) cin >> x;

// Or using istream_iterator (fancy but slower)
vector<int> a(istream_iterator<int>(cin), istream_iterator<int>());

Pattern 4: Fast Output

// Use '\n' instead of endl (endl flushes buffer)
cout << ans << '\n';  // Fast
cout << ans << endl;  // Slow - forces buffer flush

// For many outputs, build string then print once
string result;
for (int i = 0; i < n; i++) {
    result += to_string(arr[i]) + " ";
}
cout << result << '\n';

Debugging Techniques

Pattern 1: Debug Macro

#ifdef LOCAL
#define debug(x) cerr << #x << " = " << (x) << '\n'
#define debugv(v) cerr << #v << " = "; for (auto& x : v) cerr << x << ' '; cerr << '\n'
#else
#define debug(x)
#define debugv(v)
#endif

// Usage
int x = 5;
debug(x);  // Prints: x = 5 (only when compiled with -DLOCAL)

vector<int> v = {1, 2, 3};
debugv(v);  // Prints: v = 1 2 3
Compile locally with: g++ -DLOCAL solution.cpp

Pattern 2: Visualizing 2D Arrays

void print2D(vector<vector<int>>& grid) {
    cerr << "Grid:\n";
    for (auto& row : grid) {
        for (int x : row) {
            cerr << setw(4) << x;
        }
        cerr << '\n';
    }
    cerr << '\n';
}

Pattern 3: Assert for Assumptions

// Crash immediately if assumption is wrong
assert(n > 0 && "n must be positive");
assert(arr.size() == n);

// Custom assertion with message
#define ASSERT(cond, msg) \
    if (!(cond)) { cerr << "Assertion failed: " << msg << '\n'; exit(1); }

ASSERT(left <= right, "Invalid range");

Common I/O Patterns in CP

Pattern: Multi-Test Case

void solve() {
    int n;
    cin >> n;
    // Solve single test case
    cout << answer << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int t;
    cin >> t;
    while (t--) {
        solve();
    }
}

Pattern: Graph Input

// n nodes, m edges
int n, m;
cin >> n >> m;

// Adjacency list
vector<vector<int>> adj(n + 1);  // 1-indexed
for (int i = 0; i < m; i++) {
    int u, v;
    cin >> u >> v;
    adj[u].push_back(v);
    adj[v].push_back(u);  // For undirected graph
}

// Weighted edges
vector<vector<pair<int, int>>> adj(n + 1);
for (int i = 0; i < m; i++) {
    int u, v, w;
    cin >> u >> v >> w;
    adj[u].push_back({v, w});
    adj[v].push_back({u, w});
}

Pattern: Matrix Input

int n, m;
cin >> n >> m;

// 2D vector
vector<vector<int>> grid(n, vector<int>(m));
for (int i = 0; i < n; i++) {
    for (int j = 0; j < m; j++) {
        cin >> grid[i][j];
    }
}

// Character grid
vector<string> grid(n);
for (int i = 0; i < n; i++) {
    cin >> grid[i];
}
// Access: grid[i][j]

Avoiding Common Mistakes

Mistake 1: Integer Overflow

// WRONG: Overflow when n > 46340
int n = 100000;
int result = n * n;  // Overflow!

// CORRECT: Cast to long long
long long result = (long long)n * n;
// Or use ll from the start
ll n = 100000;
ll result = n * n;

Mistake 2: Array Index Out of Bounds

// WRONG: Accessing arr[n] when array has n elements
vector<int> arr(n);
cout << arr[n];  // Undefined behavior!

// CORRECT: Valid indices are 0 to n-1
cout << arr[n-1];

Mistake 3: Uninitialized Variables

// WRONG: Uninitialized
int sum;
for (int x : arr) sum += x;

// CORRECT: Initialize
int sum = 0;
for (int x : arr) sum += x;

Mistake 4: Forgetting to Reset Global Variables

// WRONG: Global variables not reset between test cases
int visited[100001];  // Global

void solve() {
    // visited still has values from previous test case!
}

// CORRECT: Reset at start of solve()
void solve() {
    memset(visited, 0, sizeof(visited));
    // or use local variables
    vector<int> visited(n + 1, 0);
}

Output Format Gotchas

Precision for Floating Point

// Default precision may not be enough
double ans = 1.0 / 3.0;
cout << ans << '\n';  // 0.333333

// Set precision
cout << fixed << setprecision(9) << ans << '\n';  // 0.333333333

Yes/No Output

// Check problem statement for exact format!
// "YES"/"NO" vs "Yes"/"No" vs "yes"/"no"
cout << (condition ? "YES" : "NO") << '\n';

Multiple Values on Same Line

// Space-separated
for (int i = 0; i < n; i++) {
    cout << arr[i] << " \n"[i == n - 1];  // Space, then newline at end
}

// Or simpler
for (int i = 0; i < n; i++) {
    if (i) cout << ' ';
    cout << arr[i];
}
cout << '\n';

Stress Testing

When your solution gets WA and you can’t find the bug:
// generate.cpp - Generates random test cases
#include <bits/stdc++.h>
using namespace std;

int main(int argc, char* argv[]) {
    srand(atoi(argv[1]));  // Seed from command line
    
    int n = rand() % 10 + 1;  // Small n for testing
    cout << n << '\n';
    for (int i = 0; i < n; i++) {
        cout << rand() % 100 << ' ';
    }
    cout << '\n';
}
# stress.sh - Run until mismatch
for ((i = 1; ; i++)); do
    ./generate $i > input.txt
    ./solution < input.txt > output1.txt
    ./brute < input.txt > output2.txt
    if ! diff -q output1.txt output2.txt > /dev/null; then
        echo "Mismatch found on test $i!"
        cat input.txt
        break
    fi
    echo "Test $i passed"
done

Key Takeaways

Always Use Fast I/O

ios::sync_with_stdio(false) and cin.tie(nullptr) are essential.

Use Debug Macros

Create debug tools that compile out for submission.

Watch for Overflow

Use long long when numbers can exceed 2×10^9.

Reset State

Clear global variables between test cases.

Next Up

Chapter 3: STL for Competitive Programming

Master the C++ Standard Template Library—the competitive programmer’s toolkit.