Writing code faster during Competitive Programming in C++

Photo by Joan Gamell on Unsplash

Writing code faster during Competitive Programming in C++

ยท

5 min read

Introduction

This is the list of modern CPP tricks often used in Coding Interviews and Competitive Programming.

1. No more nested min(x, min(y, ...))

Use initializer list and std::min and std::max to make life easy

small = min(x, min(y, min(z, k))); // the old way
small = min({x, y, z, k}); // life is easy

2. JavaScript like Destructuring using Structured Binding in C++

pair<int, int> cur = {1, 2};
auto [x, y] = cur;
// x is now 1, y is now 2
// no need of cur.first and cur.second

array<int, 3> arr = {1, 0, -1};
auto [a, b, c] = arr;
// a is now 1, b is now 0, c is now -1

3. Powerful Logging and Debugging

How debug macros work?

Straight to the point, I have often used the debug macro which stringifies the variable names and their values.

#define debug(x) cout << #x << " " << x 
int ten = 10;
debug(ten); // prints "ten = 10"

This is often useful in debugging.

The Problem with this macro - its not scalable

However, when you have multiple variables to log, you end up with more deb2 and deb3 macros.

#define debug(x) cout << #x << " " << x 
#define debug2(x) cout << #x << " " << x << " "  << #y << " " << y 
#define debug3(x, y, z) cout << #x << " " << x << " "  << #y << " " << y << " "  << #z << " " << z

This is not scalable.

Solution using a powerful macro

Here is the solution using variadic macros and fold expressions,

#define debug(...) logger(#__VA_ARGS__, __VA_ARGS__)
template<typename ...Args>
void logger(string vars, Args&&... values) {
    cout << vars << " = ";
    string delim = "";
    (..., (cout << delim << values, delim = ", "));
}

int xx = 3, yy = 10, xxyy = 103;
debug(xx); // prints "xx = 3"
debug(xx, yy, xxyy); // prints "xx, yy, xxyy = 3, 10, 103"

4. Generic Reader and Writer for multiple variables and containers

template <typename... T>
void read(T &...args) {
    ((cin >> args), ...);
}

template <typename... T>
void write(string delimiter, T &&...args) {
    ((cout << args << delimiter), ...);
}

template <typename T>
void readContainer(T &t) {
    for (auto &e : t) {
        read(e);
    }
}

template <typename T>
void writeContainer(string delimiter, T &t) {
    for (const auto &e : t) {
        write(delimiter, e);
    }
    write("\n");
}

Usage

// Question: read three space seprated integers and print them in different lines.
    int x, y, z;
    read(x, y, z);
    write("\n", x, y, z);

// even works with variable data types :)
    int n;
    string s;
    read(n, s);
    write(" ", s, "has length", n, "\n");

// Question: read an array of `N` integers and print it to the output console.
    int N;
    read(N);
    vector<int> arr(N);
    readContainer(arr);
    writeContainer(" ", arr); // output: arr[0] arr[1] arr[2] ... arr[N - 1]
    writeContainer("\n", arr);
    /**
    * output:
    * arr[0]
    * arr[1]
    * arr[2]
    * ...
    * ...
    * ...
    * arr[N - 1]
    */

5. Decorators in C++ and Multiple Parameters

Printing as many variables in one line

template<typename ...T>
void printer(T&&... args) {
    ((cout << args << " "), ...);
}

int age = 25;
string name = "Apurv";
printer("I am", name, ',', age, "years old"); 
// ^ This prints the following
// I am Apurv, 22 years old

Powerful decorator functions in C++

template<typename F>
auto debug_func(const F& func) {
    return [func](auto &&...args) { // forward reference
        cout << "input = ";
        printer(args...);
        auto res = func(forward<decltype(args)>(args)...);
        cout << "res = " << res << endl;
        return res;
    };
}

debug_func(pow)(2, 3);
// ^ this automatically prints
// input = 2 3 res = 8

Exploiting decorators by nesting them

Lets define another decorator beautify as follows.

template<typename F>
auto beautify(const F& func) {
    return [func](auto &&...args) { // forward reference
        cout << "========" << endl;
        func(forward<decltype(args)>(args)...);
        cout << "========" << endl;
    };
}

beautify(debug_func(pow(2, 3)));
// ^ this now prints
// ========
// input = 2 3 res = 8
// ========

It's amazing how much you can do by writing such generic decorators and nest them.
Think about decorators like log_time that calculates the time taken for a given function.


6. Using a Template

Using a template is one of the best ideas to speed up your implementation. Keep your template ready that has your macros written in them already. This helps in reducing a lot of your time which otherwise would have gone in writing the whole code from scratch. I use the following template, you can use one that suits you.

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef vector<int> vi;
typedef pair<int,int> pi;

#define F first
#define S second
#define PB push_back
#define MP make_pair
#define REP(i,a,b) for (int i = a; i < b; i++)
#define fastIO() ios_base::sync_with_stdio(0), cin.tie(0)

template <typename... T>
void read(T &...args) {
    ((cin >> args), ...);
}

template <typename... T>
void write(string delimiter, T &&...args) {
    ((cout << args << delimiter), ...);
}

template <typename T>
void readContainer(T &t) {
    for (auto &e : t) {
        read(e);
    }
}

template <typename T>
void writeContainer(string delimiter, T &t) {
    for (const auto &e : t) {
        write(delimiter, e);
    }
    write("\n");
}

#define debug(...) logger(#__VA_ARGS__, __VA_ARGS__)
template<typename ...Args>
void logger(string vars, Args&&... values) {
    cout << vars << " = ";
    string delim = "";
    (..., (cout << delim << values, delim = ", "));
}

const int MAX = 5E5 + 5;
const int MOD = 1E9 + 7;


void solve(){
    int tt;
    cin>>tt;
    while(tt--){
        // Your code goes here
    }
}
int main(){ 
   fastIO();
   solve();
}

Thank you for reading, please let me know if you have some possible improvements or addition.

You can now extend your support by buying me a Coffee.๐Ÿ˜Š๐Ÿ‘‡

homepage

ย