Skip to content
Embercode
✕
Contest setup and the judgeLesson 3 of 8

35 min read

A contest template

Why this matters when you build with AI

When you ask an AI tool for a solution, the code it returns often has the same shape every time: includes, a type alias, some input reading, a loop over test cases, and an output line. Most of that shape is boilerplate. If you do not know what each part does, you cannot tell when the AI left one out, added something the judge does not allow, or wrote a debug print that will break your answer.

A contest template is a starting file you keep and reuse. You write it once, test it, and then copy it for each problem. Because you test it before the contest, you know its parts work. When an AI gives you code, you can compare it against your template and spot the differences in a minute.

This lesson builds one template in C++17. C++17 is a version of the C++ language standard. Most competitive programmers use it, and the compiler flag -std=c++17 selects it. You will also see a Python version at the end, with its own notes.

The idea: a template is a fixed set of parts

A template has five jobs. Each job lives in a small block of code:

  1. Includes and namespaces. An include tells the compiler to load a library, such as the vector type or the input and output streams. The using namespace std; line lets you write vector instead of std::vector. Contest code uses one include line for the whole standard library, #include <bits/stdc++.h>. It is a GCC-specific header that pulls in almost every standard library header. It is not part of the C++ standard and some systems do not have it, so do not rely on it in a project you ship.

  2. Type aliases. An alias gives a short name to a longer type. The line using ll = long long; lets you write ll for a 64-bit signed integer. A long long holds values up to about 9.2 times 10^18, while an int usually holds only up to about 2.1 times 10^9. Competitive problems often exceed the int range, so you need the short name ready.

  3. Input and output setup. By default, C++ streams are tied to C's input and output functions, which makes reading many numbers slow. The two lines ios::sync_with_stdio(false); and cin.tie(nullptr); remove most of that cost. Lesson 02 explains this in detail, so here it is enough to know the lines belong in every template.

  4. A multi-test loop. Many problems give a first number t, then t separate test cases. The template reads t, then calls one function per case. If the problem has only one case, you set t to 1 by default and skip the read. This is a choice you make per problem, and it is a common place for mistakes, which we cover below.

  5. A debug macro that is silent on the judge. A macro is a small piece of text that the compiler replaces before it compiles the code. A debug macro prints the value of a variable to the error stream, cerr, while you test locally. The judge does not define the flag LOCAL, so the macro expands to nothing there. The printed text never reaches the judge's output, and your answer stays clean.

The output stream and the error stream are two separate channels. The judge reads only the output stream. Anything you print to cerr is ignored by the judge and shows on your screen when you run locally.

Worked example: the sample task

To test the template, we need a task. Here is a small original problem written for this lesson.

The first line holds t, the number of test cases. Each case starts with a line holding n, then a line with n integers. For each case, print the sum of the integers and the largest integer.

Here is the sample input we will use. The numbers are our own choice.

2
3
1 -4 7
2
10 20

The expected output is:

4 7
30 20

Save this template as template.cpp:

#include <bits/stdc++.h>
using namespace std;
 
using ll = long long;
using vll = vector<ll>;
using pii = pair<int, int>;
 
#ifdef LOCAL
#define dbg(x) cerr << #x << " = " << (x) << "\n"
#else
#define dbg(x) ((void)0)
#endif
 
void solve() {
    int n;
    cin >> n;
    vll a(n);
    for (auto& x : a) cin >> x;
    ll sum = 0, best = LLONG_MIN;
    for (ll x : a) {
        sum += x;
        best = max(best, x);
    }
    dbg(sum);
    cout << sum << ' ' << best << '\n';
}
 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t = 1;
    cin >> t;
    while (t--) solve();
    return 0;
}

Read the code from top to bottom. The #ifdef LOCAL block chooses one of two definitions of dbg. When you compile with -DLOCAL, the first definition is used. Inside it, #x turns the argument into a string, so dbg(sum) prints sum = followed by the value. On the judge, the second definition is used, and ((void)0) does nothing. The argument is not evaluated, so do not put code with side effects inside dbg.

The pii alias is unused in this task. Keep it in the template anyway, since many problems use pairs. The compile printed no warning about it.

The solve function reads one case and prints one line. Keep state inside it where you can. If you use a global variable, you must reset it at the start of each case, which the next section explains.

Compile and run it

We compile in two ways. The first is the local build, with the debug flag and the sanitizers. A sanitizer is a checker built into the compiler. It adds code that watches memory accesses and arithmetic while the program runs, and it stops with a report at the first error. The second is the judge-style build, with the same warning flags but no debug flag and no sanitizers.

g++ -std=c++17 -O2 -Wall -Wextra -Wshadow -Wconversion -DLOCAL -g \
    -fsanitize=address,undefined -fno-sanitize-recover=all \
    template.cpp -o template_local
./template_local < sample.in
 
g++ -std=c++17 -O2 -Wall -Wextra -Wshadow -Wconversion \
    template.cpp -o template_judge
./template_judge < sample.in

Each flag has a purpose:

  • -std=c++17 selects the C++17 language standard.
  • -O2 turns on the standard optimizations. Judges compile with optimization, so test with it too.
  • -Wall and -Wextra turn on a broad set of warnings. A warning is a message from the compiler about code that compiles but is probably wrong.
  • -Wshadow warns when an inner name hides an outer one. It is not part of -Wall or -Wextra, so you must add it.
  • -Wconversion warns when a value may lose information when converted to a smaller type, such as long long to int.
  • -DLOCAL defines the macro LOCAL, which switches on the debug printing.
  • -g adds debug information, so the sanitizer reports show line numbers.
  • -fsanitize=address,undefined turns on two sanitizers. AddressSanitizer checks memory reads and writes. UndefinedBehaviorSanitizer checks some operations the language leaves undefined, such as signed integer overflow.
  • -fno-sanitize-recover=all makes the program stop at the first error instead of trying to continue.

Our real output on GCC 15.2.0 was:

sum = 4
4 7
sum = 30
30 20

The sum = lines come from cerr. The two result lines come from cout. On a terminal they appear mixed together, which is why the judge must read only one of the two channels. The judge-style build printed only the two result lines:

4 7
30 20

Both builds compiled with no warnings, and both exited with code 0. "Compiles clean" means that the compiler prints nothing, not even a warning. Check that each time you change the template.

What the warnings and sanitizers catch

A template is only useful if the checks catch real errors. We wrote a small program with three planted bugs to test them. The program declares int n = 3;, then opens a loop that declares another int n, and it reads v[n] from a vector with 3 elements, which is out of range.

With -Wshadow, the compiler reports the inner declaration:

bad_shadow.cpp:6:13: warning: declaration of 'n' shadows a previous local [-Wshadow]
bad_shadow.cpp:4:9: note: shadowed declaration is here

Without -Wshadow, that line is not reported. This is why the flag belongs in the template.

With only -Wall -Wextra, the compiler also reports the bad index as a warning:

bad_shadow.cpp:10:15: warning: array subscript 3 is outside array bounds of 'int [3]' [-Warray-bounds=]

When you run the program under AddressSanitizer, it stops on the bad read. Only the first lines of the report are shown here. The addresses and process number differ on every run, and the full report is much longer:

==2685685==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7464fa7e001c at pc 0x5b990629070a bp 0x7fff1e95a680 sp 0x7fff1e95a670
READ of size 4 at 0x7464fa7e001c thread T0
    #0 0x5b9906290709 in main bad_shadow.cpp:10

The line heap-buffer-overflow tells you the type of error, and bad_shadow.cpp:10 tells you where. You do not need to read the rest of the report to find the bug.

A third program shows overflow on its own. It adds 1 to the largest int:

#include <bits/stdc++.h>
int main() {
    int a = 2147483647;
    int b = a + 1;
    std::cout << b << '\n';
}

Compiled with -fsanitize=undefined -fno-sanitize-recover=all, it stops with this line:

bad_overflow.cpp:4:9: runtime error: signed integer overflow: 2147483647 + 1 cannot be represented in type 'int'

Without the sanitizer, the program runs and prints a wrong value with no message. Signed overflow is undefined behaviour in C++, so the compiler may also change the result when it optimizes. The sanitizer makes the error visible.

A sanitizer reports only the errors it checks. Practice problem 5 at the end of this lesson shows a wrong answer that UndefinedBehaviorSanitizer does not report, and that the -Wconversion warning does catch. This is why the template uses both.

A Python equivalent

Python does not need a type alias, and its integers have no fixed size, so overflow is not a concern. The template is shorter:

import os
import sys
 
LOCAL = os.environ.get("LOCAL") == "1"
 
 
def dbg(name, value):
    if LOCAL:
        print(f"{name} = {value}", file=sys.stderr)
 
 
def solve(data, pos):
    n = int(data[pos])
    pos += 1
    a = list(map(int, data[pos:pos + n]))
    pos += n
    total = sum(a)
    dbg("total", total)
    return f"{total} {max(a)}", pos
 
 
def main():
    data = sys.stdin.buffer.read().split()
    t = int(data[0])
    pos = 1
    out = []
    for _ in range(t):
        line, pos = solve(data, pos)
        out.append(line)
    sys.stdout.write("\n".join(out) + "\n")
 
 
main()

Three points differ from C++. Read the whole input once with sys.stdin.buffer.read().split(), which is much faster than calling input() in a loop. Collect the output lines in a list and write them once. And run it with python3 -X dev -W error template.py. The -X dev option turns on extra runtime checks, and -W error turns Python warnings into errors. Python has no sanitizer like the one above, so these two options are the closest local check.

Our Python version printed the same two lines as the C++ version on the sample. We also compared the C++ judge-style build against a short Python brute force on 100 random inputs, each with 1 to 50 cases of 1 to 10 numbers between minus 10^9 and 10^9. All 100 matched.

Python is slower than C++ for tight loops. On this machine, 10^7 steps of a simple integer loop took about 2.7 seconds in CPython. The same loop in C++ took 133 milliseconds for 10^8 steps, so 10^7 steps would take about 13 milliseconds. Use Python for problems with small limits, and read lesson 04 before choosing a language for large inputs.

Hands-on exercise

Build your own template and check that it compiles clean. You will need a C++17 compiler. Use g++ --version to check that you have one.

  1. Create a folder for your work. Copy the template above into template.cpp.
  2. Create sample.in with the sample input from this lesson.
  3. Compile the local build with the full flag list, and check that the compiler prints nothing.
  4. Compile the judge-style build without -DLOCAL and the sanitizers, and check that it prints nothing.
  5. Run both builds on sample.in. Check that the result lines match the expected output, and that the judge-style build prints no sum = lines.
  6. Add one unused variable inside solve, such as int unused = 0;. Recompile with -Wall -Wextra. The compiler should report unused variable 'unused'. Remove the line again.
  7. Add an inner variable named n inside the for loop in solve. Recompile with and without -Wshadow, and note the difference.

Then write three of your own sample inputs for the same task. Use one case, many cases, and a case where all numbers are negative. Check each output by hand before you trust the program.

Here is a prompt you can give to an AI tool to review your template. Replace the code with your own file.

Here is my C++17 competitive programming template. The judge does not define the macro LOCAL and does not run sanitizers.
Check it for three things only:
1. Anything that prints to stdout while the judge runs.
2. Global state that is not reset between test cases.
3. Any code that changes behaviour when LOCAL is defined in a way that could change the answer.
For each problem, give the line number, a one-sentence explanation and a minimal fix.
Do not rewrite the whole file.
 
<paste template.cpp here>

A good answer points to specific lines and explains each one. It says "no problems found" when there are none, and it does not invent new bugs. Check each claim by compiling and running the code. Do not accept a claim that state is reset between cases until you have tested it with two cases where the second case is shorter than the first.

Show the answer

The exercise has no single correct answer, because your template will differ. A passing check looks like this: both builds print nothing from the compiler, the judge-style build prints only result lines, and the -Wshadow warning appears only in step 7.

Common mistakes

Printing debug text to cout. Learners often write cout << sum << endl; while debugging and then forget to remove it. The judge reads all of cout as the answer, so the extra line makes the answer wrong even when the number is right. Use dbg or cerr for all debug output.

Forgetting endl costs time. endl writes a newline and flushes the output buffer. Flushing once per line makes large outputs slow. Write '\n' instead and let the stream flush at the end of the program.

Global state that survives between test cases. A global vector keeps its contents until you clear it. If solve appends to a global vector and you do not clear it, the second case sees the first case's numbers. The symptom is an answer that is too large or too small only on the second case, which is easy to miss in a one-case sample. Keep state local to solve, or call clear() at the start of each case.

Assuming t is always present. If the problem has one case with no leading number, cin >> t reads the first number of the data as t, and everything after that is shifted by one. The output looks plausible but wrong. Check the statement for the first line before you use the loop.

Using int for sums. The sum of 10^5 values, each up to 10^9, can reach 10^14, which does not fit in an int. The ll alias avoids this, but only if you use it in every place a sum is stored. A typical mistake is to declare ll for the input array and int for the running total. The -Wconversion warning flags the assignment, as practice problem 5 shows.

Trusting "compiles" as "correct". A program can compile with no warnings and still give wrong answers. The template only checks that the code is well formed and that the common errors are caught. Test the logic with a sample, and later with a brute force, as lesson 06 shows.

Defining LOCAL inside the file. The LOCAL flag belongs on the compile line only. If you add #define LOCAL to the file, the debug macro is switched on for the judge too. The debug text still goes to cerr, so the answer stays the same, but the submitted file now depends on a setting you did not choose on the judge, and it is easy to forget.

Checklist

  • The template has one include line, one alias block, the two I/O lines, a multi-test loop and a silent debug macro.
  • The local build uses -std=c++17 -O2 -Wall -Wextra -Wshadow -Wconversion -DLOCAL -g plus the sanitizers.
  • The judge-style build uses the same flags without -DLOCAL and without the sanitizers.
  • Both builds compile with no output from the compiler.
  • Debug output goes only through dbg or cerr.
  • All state is either local to solve or cleared at the start of each case.
  • The first line of the statement has been checked against the loop.
  • Sums and products use ll in every place they are stored.

Summary

A template is a short file with the same parts every time: includes, aliases, fast I/O, a multi-test loop and a debug macro that the judge never sees. The compile line turns on warnings and sanitizers locally so that errors show up while you test, not on the judge. The Python version has the same parts, with an input read that is faster in Python and a -X dev -W error run in place of sanitizers. Keep your template, test it on a sample, and compare every AI-written solution against it.

Practice

These problems use only the template and the tools from this lesson. Write each program in your own folder and check the output before you look at the answer.

  1. Reset the state. Change the template so that it keeps a global vector<ll> seen that collects every number read in all cases, and prints the size of seen after each case. Run it on a two-case input where the second case has fewer numbers than the first. Explain what the output should be, and what it is if you forget to clear seen between cases.

    Show the answer

    The size should be the total count of numbers read so far. For the sample from this lesson, our run printed 3 and then 5, because seen keeps the first case's numbers. If the second case must print only its own count, clear seen at the start of solve. Forgetting the clear does not crash, which is why the wrong answer is easy to miss.

  2. Find the conversion warning. Write a program that reads a long long value from input and stores it in an int. Compile it with -Wall -Wextra -Wconversion, and copy the exact warning text the compiler prints. Then fix it so the warning goes away, without changing the variable's type to long long.

    Show the answer

    The compiler prints a warning with the text conversion from 'long long int' to 'int' may change value [-Wconversion]. A fix is to keep the value in a long long and only convert after checking the range, or to check the range first with an if and then convert. Changing the variable type to long long also works, but the exercise asks for a fix that keeps the int, so a range check is the expected answer.

  3. Debug output on the judge. A learner added cout << "n = " << n << '\n'; above the answer line and then submitted. Explain what the judge sees, and what the fix is.

    Show the answer

    The judge reads all of standard output as the answer. The extra line is the first line of output, so the judge compares n = 3 against the expected first line and marks the answer wrong. The fix is to change the line to dbg(n);, which prints to cerr locally and does nothing on the judge.

  4. Port the template. Write a Python version of the template that reads the sample from this lesson and prints the same output. Use sys.stdin.buffer.read().split(). Run it on the sample and check the output against the lesson.

    Show the answer

    The expected output is 4 7 on the first line and 30 20 on the second. The Python version in this lesson does this. A common mistake is to forget pos += n after the numbers of a case, which makes the next case start in the wrong place and gives a wrong or crashing result.

  5. Predict which check catches it. Take the template and change ll sum = 0, best = LLONG_MIN; to int sum = 0; ll best = LLONG_MIN;. Use the input 1, then 2, then 2000000000 2000000000. Predict whether the compile warnings, the sanitizers, or neither will report the problem. Then run the local build and the judge-style build and report what each one prints.

    Show the answer

    The compiler warns with conversion from 'll' {aka 'long long int'} to 'int' may change value [-Wconversion] on the line sum += x;. The sanitizer prints nothing, and the program prints -294967296 2000000000, which is wrong. Both builds print the same wrong line. The int sum overflows when the stored value is converted back to int. This is not the kind of overflow the sanitizer checks, so the warning is the only check that catches it. A sum in int is a common cause of wrong answers, so keep every running total in ll.

Practice this topic: coding problems for Contest setup and the judge →
← Fast input and output