How to Read Editorials & Upsolve Effectively
Every competitive programmer has been there: the contest ends, you check the scoreboard, and realize you were tantalizingly close on problem D. You open the editorial, skim through it, think "oh, that makes sense," and move on. Two weeks later you encounter the same technique and draw a blank.
The gap between reading an editorial and actually learning from it is enormous. This post is about closing that gap. We'll build a systematic approach to editorial reading, upsolving, and long-term technique retention—the meta-skills that separate plateauing competitors from those who keep climbing.
The Art of Reading Editorials
An editorial is not a solution—it's a compressed record of someone else's thought process. The goal isn't to memorize the solution; it's to reconstruct the reasoning that leads to it. This requires a deliberate, layered reading approach.
Layer 1: The High-Level Idea
On your first pass, read only the first paragraph or the "key observation" section. Stop there. Ask yourself:
- What is the core insight? Can you state it in one sentence?
- What problem structure does this insight exploit?
- Could you have discovered this insight during the contest? What would have led you there?
Don't look at the implementation yet. Give yourself 5–10 minutes to try solving the problem with just this hint. You'll be surprised how often the high-level idea alone is enough to unlock the solution.
Layer 2: The Technical Details
If the hint wasn't sufficient, continue reading. Focus on:
- The reduction step: How does the editorial transform the original problem into something solvable?
- The data structure / algorithm choice: Why this particular tool? What properties of the problem make it the right fit?
- The complexity argument: How does the author justify that the solution fits within the constraints?
Layer 3: The Implementation
Only after you understand the approach should you read the code. When you do, focus on the non-obvious parts: tricky indexing, edge case handling, the specific way a data structure is used. These are the details that trip you up during implementation.
When to Read an Editorial
Timing matters. Reading an editorial too early robs you of the struggle that builds problem-solving muscle. Reading too late wastes time you could spend learning new techniques.
The 30-60 Minute Rule
For problems at or slightly above your level, spend at least 30–60 minutes of genuine effort before opening the editorial. "Genuine effort" means:
- You've read the problem carefully and identified the constraints
- You've tried at least 2–3 different approaches
- You've worked through small examples by hand
- You've written down what you know and what's blocking you
Diminishing Returns
There's a curve to productive struggle. The first 30 minutes are almost always valuable—you're building mental models, trying approaches, and deepening your understanding of the problem space. But after 60–90 minutes with no new ideas, the returns diminish sharply:
| Time Spent | Typical Benefit | Action |
|---|---|---|
| 0–30 min | High—building understanding, testing ideas | Keep working |
| 30–60 min | Moderate—exploring deeper, refining approaches | Keep trying, consider reading a hint |
| 60–90 min | Low—often stuck in a local minimum | Read the high-level idea (Layer 1) |
| 90+ min | Very low—frustration outweighs learning | Read the full editorial |
Exception: Known Technique Gaps
If you realize early on that the problem requires a technique you've never seen (e.g., Centroid Decomposition and you don't know what that is), there's no shame in reading the editorial quickly. The goal is to learn the technique, not to reinvent it from scratch. Your struggle budget is better spent on problems where you have the tools but need to find the right combination.
The Upsolving Workflow
Upsolving—solving problems after the contest—is where most learning happens. But passive upsolving (read editorial, copy code, submit) is nearly worthless. Here is a structured workflow that actually builds skill:
Step 1: Attempt During Contest
During the contest, jot down your approach, what you tried, and where you got stuck. Even failed ideas are valuable context for later.
Step 2: Post-Contest Review (Same Day)
Within a few hours of the contest ending, while your memory is fresh:
- Read the editorial using the layered approach above
- Identify exactly what you were missing—was it the key observation, a technique, or an implementation detail?
- Write a brief note: "I was trying X, but the key was Y because Z"
Step 3: Implement Without Looking
This is the crucial step most people skip. Close the editorial and implement the solution from your understanding alone. You may need to re-read sections, but the goal is to minimize peeking. Here's a real example—suppose the editorial for a segment tree problem reveals you need lazy propagation with range assignment:
#include <bits/stdc++.h>
using namespace std;
struct SegTree {
int n;
vector<long long> tree, lazy;
vector<bool> has_lazy;
SegTree(int n) : n(n), tree(4 * n, 0),
lazy(4 * n, 0), has_lazy(4 * n, false) {}
void push_down(int node, int lo, int hi) {
if (!has_lazy[node]) return;
int mid = (lo + hi) / 2;
apply(2 * node, lo, mid, lazy[node]);
apply(2 * node + 1, mid + 1, hi, lazy[node]);
has_lazy[node] = false;
}
void apply(int node, int lo, int hi, long long val) {
tree[node] = val * (hi - lo + 1);
lazy[node] = val;
has_lazy[node] = true;
}
void update(int node, int lo, int hi, int l, int r, long long val) {
if (r < lo || hi < l) return;
if (l <= lo && hi <= r) {
apply(node, lo, hi, val);
return;
}
push_down(node, lo, hi);
int mid = (lo + hi) / 2;
update(2 * node, lo, mid, l, r, val);
update(2 * node + 1, mid + 1, hi, l, r, val);
tree[node] = tree[2 * node] + tree[2 * node + 1];
}
long long query(int node, int lo, int hi, int l, int r) {
if (r < lo || hi < l) return 0;
if (l <= lo && hi <= r) return tree[node];
push_down(node, lo, hi);
int mid = (lo + hi) / 2;
return query(2 * node, lo, mid, l, r)
+ query(2 * node + 1, mid + 1, hi, l, r);
}
void update(int l, int r, long long val) { update(1, 0, n - 1, l, r, val); }
long long query(int l, int r) { return query(1, 0, n - 1, l, r); }
};
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, q;
cin >> n >> q;
SegTree seg(n);
for (int i = 0; i < n; i++) {
long long x; cin >> x;
seg.update(i, i, x);
}
while (q--) {
int type; cin >> type;
if (type == 1) {
int l, r; long long v;
cin >> l >> r >> v;
seg.update(l, r, v);
} else {
int l, r;
cin >> l >> r;
cout << seg.query(l, r) << "\n";
}
}
}
The test: can you write this from understanding alone, without copy-pasting? If you get stuck on push_down, that tells you exactly what to reinforce.
Step 4: Submit and Verify
Submit your implementation to the online judge. Getting AC on your own code, based on your own understanding, is the real signal that you've learned something. If you get WA or TLE, debug it yourself first before re-reading the editorial.
Step 5: Revisit After 1–2 Weeks
The forgetting curve is real. Revisit the problem after a week or two and try to solve it again from scratch. If you can, the technique is genuinely in your toolkit. If you can't, you know what needs more work.
Extracting Techniques from Editorials
The highest-value outcome of reading an editorial isn't solving that specific problem—it's adding a reusable technique to your mental toolkit. Here's how to extract and retain techniques systematically.
Identify the Key Insight
Every editorial solution hinges on one or two critical observations. Train yourself to isolate them. Common categories:
- Structural insight: "The constraint graph is bipartite" or "this is equivalent to a shortest path problem"
- Algorithmic trick: "Sweep line reduces 2D to 1D" or "binary search on the answer"
- Mathematical observation: "The answer has a closed form involving Catalan numbers" or "parity argument eliminates half the cases"
- Reduction: "Problem X reduces to minimum cost flow" or "this is a disguised interval scheduling problem"
Build a Technique Vocabulary
After extracting the key insight, record it in a structured way. Over time, you build a personal technique index. Each entry should have:
| Field | Example |
|---|---|
| Technique name | Alien's trick (WQS binary search / lambda optimization) |
| When to use | DP with a constraint on the number of "groups" or "segments" used, where the unconstrained DP is concave |
| How it works | Binary search on a penalty λ added per group; the optimal λ forces exactly k groups |
| Complexity | O(n log C) where C is the value range, instead of O(n·k) |
| Reference problems | CF 868F, IOI 2014 Holiday, APIO 2014 Sequence |
Pattern Recognition Shortcuts
As your vocabulary grows, you start recognizing trigger patterns in problem statements:
- "Minimize cost with exactly k operations" → consider Alien's trick
- "Count paths in a grid" → DP, potentially with matrix exponentiation
- "Maximum/minimum over all subarrays of size k" → sliding window or monotonic deque
- "Tree with queries about paths" → HLD, Euler tour, or centroid decomposition
- "Constraints say n ≤ 20" → bitmask DP
These shortcuts don't replace thinking, but they dramatically narrow the search space for the right approach.
Learning from Others' Code
Editorials give you the idea. But reading accepted submissions from strong contestants teaches you how to implement ideas cleanly and efficiently. This is an underrated learning channel.
What to Look For
- Template structure: How do top-rated contestants organize their code? Many use compact templates that handle I/O, common macros, and data structures in a standardized way.
- Implementation tricks: Clever use of STL containers, bit manipulation shortcuts, or ways to avoid off-by-one errors.
- Different algorithmic approaches: Sometimes the editorial presents one solution, but the fastest accepted submissions use a completely different technique. This is gold—it shows you alternative ways to think about the problem.
- Edge case handling: How do strong coders handle the tricky boundary conditions? Do they use sentinels, special-case them, or structure the algorithm to avoid them entirely?
A Practical Approach
After solving a problem (or upsolving it), look at 3–5 accepted submissions from top-rated users. Compare:
- Is their approach the same as yours? If not, understand why theirs also works.
- Is their code significantly shorter or cleaner? What structural choices made that possible?
- Did they use a library or helper function you don't have in your template? Consider adding it.
Studying Coding Style
Pay attention to naming, spacing, and structure. Competitive programming code doesn't need to be beautiful, but it does need to be debuggable under pressure. If you find yourself consistently making bugs, studying how clean coders structure their implementations can reveal organizational patterns that prevent errors.
Building an Upsolving Queue
After every contest, you'll have 2–5 problems you didn't solve. Without a system, these pile up and you never get to them. An upsolving queue solves this by giving you a prioritized, trackable list of problems to work through.
Prioritization Strategy
Not all unsolved problems are equally valuable. Prioritize by:
- Just above your level: Problems you almost solved (had the right direction but missed the key step) teach the most.
- New technique exposure: Problems requiring techniques you haven't seen are high-value—even if they're hard.
- Reinforcement: If you've seen a technique once but aren't fluent, prioritize a second problem using it.
- Skip (for now): Problems that are 500+ rating points above you or require very niche techniques. Return to these later.
A Simple Tracking System
You don't need fancy tools. A simple struct and a text file work fine. Here's a lightweight C++ sketch for maintaining an upsolving log:
#include <bits/stdc++.h>
using namespace std;
struct UpsolveProblem {
string problem_id; // e.g., "CF1900E"
string contest; // e.g., "CF Round 912"
int difficulty; // estimated rating
string technique; // key technique needed
string status; // "pending", "read_editorial", "implemented", "ac", "revisited"
string notes; // what you learned
};
// Priority: problems closest to your rating come first,
// then by technique novelty
bool compare(const UpsolveProblem& a, const UpsolveProblem& b) {
int my_rating = 1600;
int gap_a = abs(a.difficulty - my_rating);
int gap_b = abs(b.difficulty - my_rating);
if (gap_a != gap_b) return gap_a < gap_b;
return a.status < b.status; // earlier status = higher priority
}
void print_queue(vector<UpsolveProblem>& queue) {
sort(queue.begin(), queue.end(), compare);
cout << "=== Upsolving Queue ===\n";
for (int i = 0; i < (int)queue.size(); i++) {
auto& p = queue[i];
cout << i + 1 << ". [" << p.status << "] "
<< p.problem_id << " (" << p.difficulty << ") - "
<< p.technique << "\n";
if (!p.notes.empty())
cout << " Notes: " << p.notes << "\n";
}
}
int main() {
vector<UpsolveProblem> queue = {
{"CF1900E", "CF Round 912", 1900, "segment tree + lazy",
"pending", ""},
{"CF1850G", "CF Round 887", 1700, "binary search on answer",
"read_editorial", "Key: monotonicity of the check function"},
{"CF1920F", "CF Round 920", 2100, "centroid decomposition",
"pending", ""},
{"CF1870D", "CF Round 898", 1800, "greedy + sorting",
"ac", "Sort by deadline, greedily assign earliest slot"},
{"CF1840E", "CF Round 880", 1600, "dp on subsets",
"implemented", "Bitmask DP, iterate over submasks"},
};
print_queue(queue);
// After upsolving, update status and add notes:
// queue[0].status = "ac";
// queue[0].notes = "Lazy prop pattern: always push before recurse";
}
The key fields are technique (what you're learning), status (where you are in the workflow), and notes (what you actually learned). The notes field is the most valuable part—it's your future self's cheat sheet.
Weekly Review Rhythm
Set a regular cadence: after each contest, add 2–3 problems to your queue. During the week, upsolve 3–5 problems from the queue. Once a month, review your notes to refresh techniques that have faded. This steady rhythm compounds over months into a massive technique repertoire.
Common Pitfalls in Upsolving
Even with a good workflow, there are traps that undermine your learning. Here are the most common ones and how to avoid them.
Pitfall 1: Copy-Paste Without Understanding
The most common and most damaging mistake. You read the editorial, copy the code (or type it while looking at the solution), get AC, and feel productive. But you've learned almost nothing—you've just practiced typing.
Fix: Always implement from understanding, not from the editorial's code. If you can't implement without looking, you don't understand it yet. Go back and re-read the approach.
Pitfall 2: Never Revisiting Problems
You upsolve a problem, get AC, and check it off forever. Two months later, you can't remember how you solved it. The Ebbinghaus forgetting curve is brutal: without review, you'll lose 60–70% of what you learned within a week.
Fix: Use spaced repetition. Revisit problems at increasing intervals: 1 day, 1 week, 1 month. You don't need to re-code the entire solution—just mentally reconstruct the approach and verify you could write it.
Pitfall 3: Over-Reliance on Editorials
Some competitors develop a reflex: if they can't solve it in 10 minutes, they reach for the editorial. This short-circuits the productive struggle phase and prevents you from developing problem-solving intuition.
Fix: Enforce a minimum struggle time. Set a timer. During that time, the editorial is off-limits. Use the 30–60 minute guideline from the When to Read section.
Pitfall 4: Upsolving Only Easy Problems
It's psychologically rewarding to upsolve problems just barely above your solve boundary—you get lots of AC's with modest effort. But you're not stretching. Real growth comes from the problems that are 200–400 rating points above your current level.
Fix: In your upsolving queue, maintain a ratio. For every 3 problems near your level, upsolve 1–2 that are significantly harder. These harder problems won't yield quick AC's, but they'll expose you to new techniques and thinking patterns.
Pitfall 5: Not Taking Notes
You solve the problem, feel good, and move on. A month later, someone asks "How do you approach segment tree problems with range operations?" and you have no structured answer.
Fix: After every upsolve, write at least one sentence: the key insight, the technique, or the implementation trick you learned. These notes compound into an invaluable personal reference.
Pitfall 6: Treating All Editorials Equally
Some editorials are excellent—they explain the thought process, the failed approaches, and why the solution works. Others just dump code with a one-line explanation. If you're learning from a bad editorial, supplement with blog posts, tutorial videos, or forum discussions about the problem.
Fix: If the official editorial is unclear, search for community explanations on Codeforces blogs, competitive programming YouTube channels, or discussion threads. Multiple perspectives often clarify what a single editorial obscures.