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:
-
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 writevectorinstead ofstd::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. -
Type aliases. An alias gives a short name to a longer type. The line
using ll = long long;lets you writellfor a 64-bit signed integer. Along longholds values up to about 9.2 times 10^18, while anintusually holds only up to about 2.1 times 10^9. Competitive problems often exceed theintrange, so you need the short name ready. -
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);andcin.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. -
A multi-test loop. Many problems give a first number
t, thentseparate test cases. The template readst, then calls one function per case. If the problem has only one case, you settto 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. -
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 flagLOCAL, 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 holdingn, then a line withnintegers. 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 20The expected output is:
4 7
30 20Save 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.inEach flag has a purpose:
-std=c++17selects the C++17 language standard.-O2turns on the standard optimizations. Judges compile with optimization, so test with it too.-Walland-Wextraturn on a broad set of warnings. A warning is a message from the compiler about code that compiles but is probably wrong.-Wshadowwarns when an inner name hides an outer one. It is not part of-Wallor-Wextra, so you must add it.-Wconversionwarns when a value may lose information when converted to a smaller type, such aslong longtoint.-DLOCALdefines the macroLOCAL, which switches on the debug printing.-gadds debug information, so the sanitizer reports show line numbers.-fsanitize=address,undefinedturns 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=allmakes 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 20The 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 20Both 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 hereWithout -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:10The 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.
- Create a folder for your work. Copy the template above into
template.cpp. - Create
sample.inwith the sample input from this lesson. - Compile the local build with the full flag list, and check that the compiler prints nothing.
- Compile the judge-style build without
-DLOCALand the sanitizers, and check that it prints nothing. - Run both builds on
sample.in. Check that the result lines match the expected output, and that the judge-style build prints nosum =lines. - Add one unused variable inside
solve, such asint unused = 0;. Recompile with-Wall -Wextra. The compiler should reportunused variable 'unused'. Remove the line again. - Add an inner variable named
ninside theforloop insolve. 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 -gplus the sanitizers. - The judge-style build uses the same flags without
-DLOCALand without the sanitizers. - Both builds compile with no output from the compiler.
- Debug output goes only through
dbgorcerr. - All state is either local to
solveor cleared at the start of each case. - The first line of the statement has been checked against the loop.
- Sums and products use
llin 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.
-
Reset the state. Change the template so that it keeps a global
vector<ll> seenthat collects every number read in all cases, and prints the size ofseenafter 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 clearseenbetween 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
3and then5, becauseseenkeeps the first case's numbers. If the second case must print only its own count, clearseenat the start ofsolve. Forgetting the clear does not crash, which is why the wrong answer is easy to miss. -
Find the conversion warning. Write a program that reads a
long longvalue from input and stores it in anint. 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 tolong 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 along longand only convert after checking the range, or to check the range first with anifand then convert. Changing the variable type tolong longalso works, but the exercise asks for a fix that keeps theint, so a range check is the expected answer. -
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 = 3against the expected first line and marks the answer wrong. The fix is to change the line todbg(n);, which prints tocerrlocally and does nothing on the judge. -
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 7on the first line and30 20on the second. The Python version in this lesson does this. A common mistake is to forgetpos += nafter the numbers of a case, which makes the next case start in the wrong place and gives a wrong or crashing result. -
Predict which check catches it. Take the template and change
ll sum = 0, best = LLONG_MIN;toint sum = 0; ll best = LLONG_MIN;. Use the input1, then2, then2000000000 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 linesum += x;. The sanitizer prints nothing, and the program prints-294967296 2000000000, which is wrong. Both builds print the same wrong line. Theintsum overflows when the stored value is converted back toint. This is not the kind of overflow the sanitizer checks, so the warning is the only check that catches it. A sum inintis a common cause of wrong answers, so keep every running total inll.