Contest Strategy
Interviews reward depth on one problem at a time. Contests reward breadth and speed across several problems in a fixed window, with a scoreboard ticking the whole time. The algorithms are the same ones from earlier phases — what changes is the meta-game: which problem to read first, how long to spend before giving up, and how to debug when the clock is the real opponent.
What you’ll learn
Section titled “What you’ll learn”- Why constraints in the problem statement are a spoiler for the intended time complexity.
- How to manage time across a contest: what to attempt first, when to skip.
- The LeetCode contest workflow — format, rating, virtual contests.
- The Codeforces workflow — divisions, rating, virtual contests, and upsolving.
- How to debug under time pressure and stress test a solution against a brute force.
- Why having a template ready before the contest starts saves real minutes.
Complexity budgets from constraints
Section titled “Complexity budgets from constraints”Every well-set contest problem gives you (and sometimes a time limit) specifically so you can reverse-engineer the intended approach before writing any code. This is the single highest-leverage reading skill in competitive programming.
| Constraint on n | Expected complexity | Typical approach |
|---|---|---|
| or | Brute-force permutations or subset enumeration | |
| — | Bitmask DP, subset enumeration | |
| Triple nested loops, Floyd-Warshall | ||
| DP tables, pairwise comparisons | ||
| — | Sorting, heaps, segment trees, binary search | |
| — | Linear scans, two pointers, prefix sums | |
| or | Binary search on the answer, matrix exponentiation, closed-form math |
The budget, adjusted for Python
Section titled “The budget, adjusted for Python”The table above is the standard one, and it assumes a C++-speed constant factor. Pure-Python loops run roughly 10-100x slower, so shift the practical figure down about one order of magnitude: simple operations per second is a safer working number than .
What does not pay that tax is work pushed into C:
| Slow in Python | Fast equivalent | Why |
|---|---|---|
for loop summing a list | sum(lst) | The loop runs in C |
Building a string with += | "".join(parts) | Avoids quadratic reallocation |
x in some_list | x in some_set | becomes |
lst.pop(0) / lst.insert(0, x) | collections.deque | becomes |
| Hand-written sort or heap | sorted, heapq, bisect | All C implementations |
input() in a loop | sys.stdin.readline or one sys.stdin.read().split() | See Phase 2’s Fast IO page |
So an solution at is comfortable in C++ and marginal in CPython if the inner work is Python-level. The usual fixes, in order of how much they buy: switch to PyPy where the judge offers it, push the hot loop into a built-in, or accept a worse asymptotic bound with a much better constant.
Diagnosing a TLE
Section titled “Diagnosing a TLE”A time-limit-exceeded verdict has exactly three causes, and they need different responses:
- The complexity class is wrong. You wrote where the constraint demanded . Re-read the constraint block — it told you the intended bound before you started.
- The class is right and the constant is not. Python-level loops, per-iteration allocation,
string concatenation,
inon a list. Same algorithm, C-level primitives. - The bound is not over what you think. Binary search on the answer is in the
value range; knapsack is in the capacity. Both are routinely quoted as functions of
nalone, and both blow up when the other variable is large.
Check 1 and 3 before rewriting anything. Rewriting for constant factor when the class is wrong is the most expensive mistake available in a timed contest.
Time management
Section titled “Time management”A contest is a scoring problem, not a correctness problem in isolation — partial credit for fast, safe points usually beats a heroic attempt at the hardest problem on the set.
- Read every problem’s statement and constraints first, in order, before committing to solve any one of them — five minutes of reconnaissance can save twenty minutes of solving the wrong problem first.
- Solve in roughly increasing difficulty, but skip ahead if an early problem isn’t clicking — points from a later, easier-for-you problem count the same as points from an earlier one.
- Set a mental time box per problem. If you haven’t found an approach in the time you’ve budgeted, move on and come back later rather than spiraling.
- Bank the easy points before chasing the hard ones. A contest lost on a silly bug in problem A while problem D sits unread is a worse outcome than a clean sweep of A through C.
graph TD
N0["Reading a problem"] --> N1{"Do I see a pattern within ~5 minutes?"}
N1 -- Yes --> N2["Implement it"]
N1 -- No --> N3{"Have easier unsolved problems left?"}
N3 -- Yes --> N4["Skip -- solve those first"]
N3 -- No --> N5["Keep pushing on this one"]
N4 --> N6["Return here later with fresh eyes"]
LeetCode contest workflow
Section titled “LeetCode contest workflow”- Format: Weekly and Biweekly contests, four problems in 90 minutes, roughly increasing in difficulty and points.
- Rating: an ELO-style number that moves based on your rank relative to other participants each contest — consistency across many contests matters more than any single result.
- Penalties: wrong submissions cost time-adjusted points, so test locally before submitting rather than using the judge as your debugger.
- Virtual contests: missed a live contest? Take it later under timed conditions — the practice value is nearly identical even though it doesn’t count toward rating.
- After the contest: read editorial solutions for anything you didn’t finish, and upsolve (solve it properly afterward) rather than just reading the answer.
Codeforces workflow
Section titled “Codeforces workflow”- Divisions: Div. 4 and Div. 3 target newer competitors with gentler early problems; Div. 2 is the mainstream bracket; Div. 1 (and combined Div. 1 + 2 rounds) target higher-rated competitors. Pick the division that matches your current rating.
- Rating: also ELO-style, but with a longer history and more established bands (see the roadmap page for rating-to-focus mapping).
- Virtual contests: Codeforces lets you run any past contest as a timed virtual round, complete with the original problems and time limit — one of the best practice tools available, since it simulates real contest pressure on demand.
- Upsolving: after a round ends, keep working on problems you didn’t finish using the same submission system — this is where most rating gains actually come from over time, not from the live round itself.
Debugging under time pressure
Section titled “Debugging under time pressure”- Isolate the failing function and test it directly with the sample input rather than re-running the whole program repeatedly.
- Check the boundaries first: , , all-equal values, and the largest allowed input — boundary bugs are the most common source of wrong answers in a rushed solution.
- Look for off-by-one errors around loop bounds and array indices before assuming the algorithm is wrong — most contest bugs are implementation bugs, not conceptual ones.
- Print intermediate state sparingly and remove it before submitting
— a stray debug
printcan silently break judges that check output exactly.
Stress testing
Section titled “Stress testing”When a solution passes the sample cases but fails a hidden test, generate random small inputs, run both a brute-force reference and your fast solution, and compare outputs until you find a mismatch.
import random
def brute_force(nums, target):
for i in range(len(nums)):
for j in range(i + 1, len(nums)):
if nums[i] + nums[j] == target:
return sorted((i, j))
return None
def fast_solution(nums, target):
seen = {}
for i, value in enumerate(nums):
complement = target - value
if complement in seen:
return sorted((seen[complement], i))
seen[value] = i
return None
def is_valid(nums, target, answer, reference):
"""Two Sum has many valid answers, so check validity -- not equality."""
if answer is None:
return reference is None # only None when no pair exists at all
i, j = answer
return i != j and nums[i] + nums[j] == target
for trial in range(200):
size = random.randint(2, 8)
nums = [random.randint(-10, 10) for _ in range(size)]
target = random.randint(-10, 10)
reference = brute_force(nums, target)
actual = fast_solution(nums, target)
if not is_valid(nums, target, actual, reference):
print("MISMATCH on trial", trial, nums, target, "reference", reference, "got", actual)
break
else:
print("All 200 random trials matched.")Template preparation
Section titled “Template preparation”Walking into a contest with a ready-made template — fast input reading
(see Phase 2’s Fast IO and Beating TLE), common imports (heapq,
collections.deque, bisect, math.gcd), and a scaffold for reading
multiple test cases — saves real minutes on every single problem, not
just the hard ones.
Contest-day checklist
Section titled “Contest-day checklist”| Before the contest | During the contest |
|---|---|
| Templates for fast IO and common imports ready to paste | Read constraints before committing to an approach |
| Editor/IDE shortcuts and run configuration tested | Solve roughly in order, skip if stuck past your time box |
| Know the judge’s time and memory limits | Test locally against samples before submitting |
| Full night’s sleep — contest performance drops sharply when tired | Stress test against a brute force if a submission fails silently |
| Warm up with one easy problem beforehand | Upsolve unfinished problems immediately after, while it’s fresh |
Pitfalls
Section titled “Pitfalls”- Using the judge as a debugger. Wrong submissions cost time-adjusted points on LeetCode and a flat penalty on Codeforces. Test locally against the samples, then submit once. A submit-and-see loop is the most expensive habit in competitive programming.
- Comparing outputs in a stress test when the answer is not unique. The single most common stress-testing mistake, and it wastes the technique’s whole value — you chase a “bug” that is two equally valid answers. See the caution in the stress-testing section below; compare a canonical value, or validate the answer rather than matching it.
- Chasing an trick when . The constraint said was intended. Twenty minutes hunting for elegance while problems C and D sit unread is a strictly worse outcome than a fast, correct quadratic solution.
- Not reading every statement first. Five minutes of reconnaissance routinely reveals that problem C is easier for you than problem B. Points are points; the letters are not a ranking of your difficulty.
- Spiralling on one problem. Without a time box, one hard problem eats the contest. If no approach has appeared in your budgeted time, leave — and come back with fresh eyes, which genuinely works.
- Assuming the algorithm is wrong when the bug is an off-by-one. Most contest failures are
implementation bugs, not conceptual ones. Check loop bounds, inclusive-versus-exclusive ranges,
and the
n = 0/n = 1cases before you throw the approach away. - A stray debug
printleft in. Judges compare output exactly, so a leftover trace is a wrong answer on a correct solution. Print tosys.stderrif you must print at all — judges ignore it. - Forgetting to reset global state between test cases. A multi-test-case problem with a
module-level
visitedset or a memo that persists across cases fails from the second case on — and passes the sample, which usually has one case. Reset inside the per-case function. - Reading input with
input()in a loop. At lines this alone can TLE a correct solution.sys.stdin.readline, or read everything at once and split. - Integer division and negative numbers. Python’s
//floors, so-7 // 2is-4, not-3as C++ gives. If a problem’s formula was written with C++ truncation in mind, useint(a / b)ormath.trunc, and be careful with%too — Python’s result takes the divisor’s sign. - Never upsolving. Reading the editorial is not upsolving. Most rating gain comes from finishing the problems you could not finish, in the judge, afterwards.
- Only ever competing live. Virtual contests on past rounds reproduce the same time pressure on demand, and they are the most underused practice tool available.
🧪 Try It Yourself
Section titled “🧪 Try It Yourself”Practice
Section titled “Practice”Contest-shaped problems: the ones where reading the constraints tells you the intended complexity before you have an algorithm. Practise deriving the target bound first.
Work down the ladder. Tick each problem off as you go — progress is saved in this browser, and the Export button in the filter bar writes it to a file you can keep.
- 26Remove Duplicates from Sorted Arrayeasy
- 27Remove Elementeasy
- 69Sqrt(x)easy
- 88Merge Sorted Arrayeasy
- 125Valid Palindromeeasy
- 283Move Zeroeseasy
- 392Is Subsequenceeasy
- 1046Last Stone Weighteasy
- 153Summedium
- 11Container With Most Watermedium
- 347Top K Frequent Elementsmedium
- 621Task Schedulermedium
- 875Koko Eating Bananasmedium
- 80Remove Duplicates from Sorted Array IImedium
- 167Two Sum II - Input Array Is Sortedmedium
- 204Count Primesmedium
- 215Kth Largest Element in an Arraymedium
- 264Ugly Number IImedium
- 279Perfect Squaresmedium
- 1011Capacity To Ship Packages Within D Daysmedium
- 1390Four Divisorsmedium
- 1631Path With Minimum Effortmedium
- 42Trapping Rain Waterhard
- 23Merge k Sorted Listshard
- 410Split Array Largest Sumhard
- 774Minimize Max Distance to Gas Stationpremiumhard
- 1851Minimum Interval to Include Each Queryhard
Interview follow-ups
Section titled “Interview follow-ups”Contests are not interviews, but the two questions below come up constantly — once from interviewers about your competitive background, and once from yourself when a submission fails.
| The question | What it is really asking | The answer |
|---|---|---|
| “You compete. How does that help here?” | Whether you can frame it usefully | Implementation speed and debugging under pressure, plus reading constraints to size an approach. Not “it makes me a better engineer” — that claim invites disagreement and is not what contests train |
| “Contest code is not production code. Do you know the difference?” | Self-awareness | Yes, and name the specifics: single-letter names, no validation, global state, no tests. Contest code optimises for minutes-to-correct; production code optimises for the next reader. Both are correct in context |
| “Your submission TLE’d. What do you check first?” | Diagnosis order | Whether the class is wrong, then whether the bound is over the variable you assumed (, ), and only then the constant factor. Rewriting for constant when the class is wrong wastes the round |
| “Wrong answer, but the samples pass. Now what?” | Method over guessing | Stress test against a brute force on small random inputs — and validate rather than compare when the answer is not unique. Also check the boundaries: n = 0, n = 1, all-equal, maximum size |
| “Why not just resubmit and see?” | Cost awareness | Penalties are real: time-adjusted points on LeetCode, a flat penalty on Codeforces. The judge is a grader, not a debugger, and treating it as one loses more points than the bug did |
| “How do you decide to abandon a problem?” | Whether you have a rule or a feeling | A pre-set time box. If no approach has appeared within it and easier problems remain unsolved, leave and return later. Points are points, and the letters are not ordered by your difficulty |
| “How do you actually improve?” | Whether practice is deliberate | Upsolving in the judge after the round — not reading editorials — plus virtual contests on past rounds for time-pressure reps. Most rating gain comes from finishing what you could not finish live |
Self-check
Section titled “Self-check”-
A problem gives n <= 5,000. What should you conclude?
5,000 squared is 25 million, comfortably inside a one-second budget even in Python if the inner work is cheap. A setter who wanted O(n log n) would have written 10^5 or larger. Spending twenty minutes hunting for elegance while later problems sit unread is a strictly worse outcome than a fast, correct quadratic.
pch.quizShowAnswer
B — O(n^2) is very likely the intended solution -- implement it correctly and quickly — 5,000 squared is 25 million, comfortably inside a one-second budget even in Python if the inner work is cheap. A setter who wanted O(n log n) would have written 10^5 or larger. Spending twenty minutes hunting for elegance while later problems sit unread is a strictly worse outcome than a fast, correct quadratic.
-
Your stress test compares brute_force and fast_solution outputs for Two Sum and reports a mismatch on trial 29. What is the most likely explanation?
Written with direct comparison, this exact harness fires on 8 of 200 trials. On [3, 1, 2, 0] with target 3, the brute force returns [0, 3] and the hash version returns [1, 2] -- both correct. Whenever the answer is not unique, either validate the answer against the problem's condition or make both functions return something canonical, such as a count.
pch.quizShowAnswer
B — Two Sum has many valid answers, and the two functions found different correct pairs — Written with direct comparison, this exact harness fires on 8 of 200 trials. On [3, 1, 2, 0] with target 3, the brute force returns [0, 3] and the hash version returns [1, 2] -- both correct. Whenever the answer is not unique, either validate the answer against the problem's condition or make both functions return something canonical, such as a count.
-
When is direct output comparison the right way to stress test?
Uniqueness is the precondition. "Return the maximum sum" has one answer; "return any pair summing to k" has many, and comparing those will eventually produce a mismatch that is not a bug. Getting a false positive on the first mismatch is the usual reason people conclude stress testing does not work and stop using it.
pch.quizShowAnswer
B — Only when the answer is genuinely unique: a maximum value, a count, a sorted list — Uniqueness is the precondition. "Return the maximum sum" has one answer; "return any pair summing to k" has many, and comparing those will eventually produce a mismatch that is not a bug. Getting a false positive on the first mismatch is the usual reason people conclude stress testing does not work and stop using it.
-
A correct O(n log n) solution TLEs in Python. What do you check first?
Cheapest checks first. The class may simply be wrong -- re-read the constraint, which told you the intended bound before you started. Then check the variable: binary search on the answer is O(n log R) in the *value* range and knapsack is O(nW) in the *capacity*, both routinely misquoted as functions of n. Only after those does constant-factor work make sense; rewriting for constant when the class is wrong is the most expensive mistake in a timed contest.
pch.quizShowAnswer
B — Whether the complexity class is actually wrong, and whether the bound is over the variable you assumed — Cheapest checks first. The class may simply be wrong -- re-read the constraint, which told you the intended bound before you started. Then check the variable: binary search on the answer is O(n log R) in the *value* range and knapsack is O(nW) in the *capacity*, both routinely misquoted as functions of n. Only after those does constant-factor work make sense; rewriting for constant when the class is wrong is the most expensive mistake in a timed contest.
-
A multi-test-case problem passes the sample but fails from the second case onward on the judge. Most likely cause?
The signature is passing case one and failing the rest, and the sample usually has a single case so it hides the bug completely. An `lru_cache` that survives across cases is the same failure in a different costume. Reset state inside the per-case function, or build it fresh each time.
pch.quizShowAnswer
B — Global or module-level state -- a visited set, memo, or counter -- is not reset between cases — The signature is passing case one and failing the rest, and the sample usually has a single case so it hides the bug completely. An `lru_cache` that survives across cases is the same failure in a different costume. Reset state inside the per-case function, or build it fresh each time.
-
In Python, `-7 // 2` evaluates to what, and why does it matter in contests?
Python floors toward negative infinity; C++ truncates toward zero. Translate a formula from an editorial written in C++ and every negative input silently drifts by one. The same asymmetry applies to `%`: Python's result takes the sign of the divisor, so -7 % 2 is 1, not -1.
pch.quizShowAnswer
B — -4, because Python floors -- so a formula written with C++ truncation in mind needs int(a / b) or math.trunc — Python floors toward negative infinity; C++ truncates toward zero. Translate a formula from an editorial written in C++ and every negative input silently drifts by one. The same asymmetry applies to `%`: Python's result takes the sign of the divisor, so -7 % 2 is 1, not -1.
-
What produces the most rating gain over time?
Live rounds measure; practice improves. Upsolving means actually getting the accepted verdict on what you could not finish -- reading the editorial and nodding is not the same thing, because it skips the implementation, which is where contest failures actually live. Virtual contests reproduce time pressure on demand and are the most underused tool available.
pch.quizShowAnswer
B — Upsolving in the judge after the round, plus virtual contests on past rounds — Live rounds measure; practice improves. Upsolving means actually getting the accepted verdict on what you could not finish -- reading the editorial and nodding is not the same thing, because it skips the implementation, which is where contest failures actually live. Virtual contests reproduce time pressure on demand and are the most underused tool available.
Recall card
Section titled “Recall card”- Read the constraints before the statement. They name the intended complexity, and that prunes the approach space in seconds.
nto bound: <=10 factorial · <=22 · <=500 · <=5,000 · <= · <= · or a formula.- Shift the budget down ~10x for pure Python. operations per second, not — work
pushed into C (
sum,sorted,heapq,set,join) does not pay the tax. - Read every problem first. C is often easier for you than B. Points are points.
- Time-box each problem and leave when it expires. Returning with fresh eyes works.
- When , write the . The constraint said so.
- Never use the judge as a debugger — penalties are real. Test locally, submit once.
- Stress test with a brute force, and validate rather than compare when the answer is not unique — otherwise the first mismatch is a false positive and you abandon the technique.
- TLE diagnosis order: wrong class -> wrong variable (, ) -> constant factor. Never reverse it.
- Reset global state between test cases. Passing case 1 and failing the rest is the signature.
-7 // 2is-4, not-3. Python floors; C++ truncates. Same for%.- Strip debug prints — judges compare exactly. Use
sys.stderrif you must. - Upsolve in the judge, not in the editorial, and use virtual contests for time-pressure reps. That is where rating actually comes from.
- Constraints are a spoiler for the intended complexity — read them before committing to an approach.
- Solve roughly in order of difficulty, skip when stuck, and bank easy points before chasing hard ones.
- LeetCode and Codeforces both reward consistent participation over single big results — use virtual contests and upsolving between rounds.
- When a solution fails silently, stress test it against a brute-force reference with random small inputs rather than guessing at the bug.
Next: Study Plans and Roadmap — turning this whole site’s phases into a week-by-week interview prep plan and a competitive-programming ladder.
pch.coffeeTagline
pch.coffeeCtapch.feedbackHeading
pch.feedbackSubheading