← All Posts
CP Math · Contest Strategy· Part 11 of 12

Practice Methodology — Deliberate Practice, Topic Rotation & Spaced Repetition

Talent is overrated in competitive programming. The difference between a 1200-rated coder and a 1900-rated one is almost never raw intelligence — it is how they practice. Most people grind hundreds of problems on autopilot and wonder why their rating plateaus. This post gives you the concrete system to change that.

We cover seven pillars of effective CP practice: deliberate practice, topic rotation, spaced repetition, the difficulty ladder, virtual contests, training schedules, and measurement. Each section ends with an actionable takeaway.

Deliberate Practice vs Grinding

The psychologist Anders Ericsson coined the term deliberate practice to describe a very specific kind of training: activities designed to improve performance, with immediate feedback, that push you just beyond your current ability. Mindlessly solving 100 easy problems is not deliberate practice. Struggling with 10 problems at the edge of your skill, analysing every wrong attempt, and extracting transferable patterns — that is.

The Grinding Trap

Grinding feels productive because you see the "Accepted" verdict often. But if every problem takes you under 10 minutes and you never get stuck, you are reinforcing what you already know. Ericsson's research shows that true skill growth only happens in the "zone of proximal development" — problems that are hard enough to make you think but not so hard that you cannot make progress at all.

✗ Grinding

  • Solving problems well below your rating
  • Never reading editorials — or reading them too soon
  • Moving on immediately after AC
  • No post-solve reflection
  • Random topic selection

✓ Deliberate Practice

  • Targeting problems at rating + 100 to + 300
  • Spending 30–60 min before looking at hints
  • Writing down why your approach works after AC
  • Upsolving every contest problem you missed
  • Structured topic rotation

The Post-Solve Protocol

After solving (or upsolving) a problem, spend 5 minutes on this checklist:

  1. Tag it. What topic(s) did this problem test? (DP, graphs, math, greedy, etc.)
  2. Rate difficulty. Was it easy / medium / hard for you?
  3. Identify the key insight. What was the one idea that cracked the problem?
  4. Note mistakes. Did you get WA first? What went wrong?
  5. Generalise. What class of problems does this insight apply to?
Actionable takeaway: Keep a "problem log" — a simple spreadsheet or text file where each row records the problem link, topic tags, difficulty, key insight, and mistakes. This log becomes the raw material for spaced repetition later.

The 80/20 Rule of Practice

Roughly 80% of your practice time should be spent on problems that genuinely challenge you (struggle zone). The remaining 20% can go toward speed drills on familiar topics — this builds implementation fluency without stagnating your problem-solving skills. If you solve 5 problems in a session, 4 should make you sweat and 1 can be a warm-up.

Topic Rotation Strategy

One of the biggest mistakes in CP training is obsessing over one topic for weeks. You finish a DP course, feel like a DP master, then freeze on a graph problem in a contest. The solution is structured topic rotation — a systematic cycle through all major topics so that no skill atrophies.

The 4-Week Rotation

A proven template is a 4-week cycle that covers the core CP pillars. Each week you focus primarily on one topic family but sprinkle in 1–2 problems from other topics to maintain breadth.

WeekPrimary Focus (70%)Secondary (20%)Warm-up (10%)
1Dynamic ProgrammingMath / Number TheoryImplementation
2Graphs & TreesDP (review)Greedy
3Math & CombinatoricsGraphs (review)Strings
4Data StructuresMath (review)Binary Search

After completing one cycle, shift the secondary column to cover topics you scored lowest on during the previous cycle. This creates a natural feedback loop where weaknesses get extra attention.

Tracking Topic Balance

Use your problem log to compute a topic distribution at the end of each month. If more than 40% of your solves are from a single topic, you are over-indexing. The ideal distribution for a well-rounded contestant looks like this:

Weakness-first rotation: If your Codeforces profile shows 30% solve rate on graph problems but 70% on DP, swap the week order — lead with graphs. The fastest way to gain rating is to patch your weakest topic, not to double down on your strongest.

Micro-Rotation Within a Day

Even within a single practice session, avoid doing 5 problems from the same topic in a row. A better pattern is: A-B-A-B-C where A is your focus topic, B is a review topic, and C is a warm-up. This interleaving forces your brain to context-switch between problem types — exactly what happens in a real contest.

Spaced Repetition for Algorithms

Spaced repetition is the most evidence-backed learning technique in cognitive science. Review material at increasing intervals — 1 day, 3 days, 1 week, 2 weeks, 1 month, 3 months — right before you are about to forget it. This transforms short-term familiarity into permanent recall.

Applying SRS to CP

Most people associate spaced repetition with flashcards (Anki), but in CP you can apply the same principle to problems. Here is the system:

  1. Day 0: Solve (or upsolve) a problem. Log it with topic tags and the key insight.
  2. Day 1: Without looking at your old code, re-read the problem statement. Can you recall the approach within 2 minutes? If yes, schedule the next review for Day 3. If no, re-solve it and schedule for Day 1 again.
  3. Day 3: Same recall test. Pass → schedule for Day 7. Fail → reset to Day 1.
  4. Day 7: Pass → Day 14. Fail → Day 3.
  5. Day 14 → Day 30 → Day 90: Continue the pattern. After Day 90, the problem is "graduated" — the pattern is permanent.
You do not need to re-code every review. For most reviews, a mental walk-through of the algorithm is sufficient. Only re-code if you cannot recall the approach or if the implementation had tricky details (e.g., segment tree with lazy propagation).

Building a Review Queue

Keep a simple list — a text file, spreadsheet, or Anki deck — where each entry is:

Problem: CF 1492D - Genius's gambit
Topic:   Combinatorics, Greedy
Insight: Count inversions via prefix sums on bit positions
Next review: 2026-06-15 (Day 7)

Each morning, filter for entries whose review date is today. Spend 15–20 minutes on recall before your main practice session. This "warm-up review" primes your brain with patterns from multiple topics before you tackle new problems.

What to Put on Flashcards

Not every problem deserves a flashcard. Focus on problems where:

A good Anki card front might be: "How do you count distinct necklaces with n beads and k colours under rotation?" Back: "Burnside's lemma — average the number of colourings fixed by each rotation. Fixed by rotation by d = k^gcd(d,n)."

The Difficulty Ladder

Your goal is always to solve problems at the edge of your ability. The difficulty ladder gives you a concrete rule: target problems rated your current rating + 100 to + 300 on Codeforces.

Progressive Overload

Borrowed from strength training, progressive overload means gradually increasing the difficulty as you adapt. Here is a practical protocol:

  1. Baseline: For one week, solve 10 problems at your rating + 200. Track your solve rate (how many you get without hints) and average time.
  2. Threshold: Once your solve rate at a given difficulty hits 60–70% (i.e., you solve 6–7 out of 10 independently), it is time to move up by 100 rating points.
  3. Deload week: Every 4th week, drop back to your rating + 100 and focus on speed and clean implementations. This prevents burnout and consolidates gains.
Mapping across platforms: CF 1200 ≈ AtCoder ABC C–D ≈ LeetCode Medium. CF 1600 ≈ AtCoder ABC E–F ≈ LeetCode Hard. CF 2000 ≈ AtCoder ARC B–C ≈ LeetCode Hard+. Use these rough equivalences when mixing platforms.

When You Are Stuck

Being stuck is not a bug — it is the feature. But there is a right way to be stuck:

  1. 0–15 min: Read the problem carefully. Try brute-force on small cases by hand.
  2. 15–30 min: Try at least 2–3 different approaches. Write pseudocode. Draw examples.
  3. 30–45 min: Look at hints (first hint only). Does it unlock a new direction?
  4. 45–60 min: Read the editorial. Understand the solution. Then close it and implement from memory.
  5. > 60 min: If you still cannot implement it, the problem is too far above your level. Drop down 100 points and come back in 2 weeks.

The Rating Plateau

Every CP practitioner hits plateaus — periods where rating stagnates for weeks or months. Plateaus usually mean one of three things:

Virtual Contests

Virtual contests are the single most underrated training tool in competitive programming. Participate in a past contest as if it were live — same time limit, same problem set, no peeking at standings or editorials until the clock runs out. Both Codeforces and AtCoder support this natively.

Why Virtual Contests Matter

Solving problems with no time pressure exercises different cognitive muscles than solving under a 2-hour clock. In a contest you must triage (pick problems wisely), time-box (know when to move on), handle pressure, and debug fast. None of these develop during leisurely upsolving.

How to Run Virtual Contests Effectively

  1. Frequency: 2–3 virtual contests per week. Treat them as seriously as rated rounds.
  2. Selection: Pick contests from 3–6 months ago at your division level. Avoid contests where you have already seen the problems.
  3. Environment: Close all distractions. Use the same setup you would use in a real contest — your template, your IDE, your compilation script.
  4. Full duration: Do not stop early even if you are stuck. In a real contest you would keep trying. Practice that persistence.
  5. Post-contest upsolve: After the virtual contest, upsolve every problem you did not AC. This is where the real learning happens.
Codeforces tip: On any past contest page, click "Virtual participation" in the sidebar. It gives you a fresh timer, tracks your submissions, and even shows your virtual ranking compared to the original participants.

Simulating Contest Pressure

To make virtual contests feel more real:

Training Schedules

Knowing what to practice is only half the battle. You also need to know when and how much. Below are sample schedules. Adjust hours to fit your life — consistency matters more than volume.

Beginner (Rating < 1200) — 7–10 hrs/week

DayActivityDuration
MonTopic study: read tutorial + solve 3 easy problems1.5 hr
TueSolve 3–4 problems at rating + 1001.5 hr
WedVirtual contest (Div 2)2 hr
ThuUpsolve contest problems + review log1 hr
FriSolve 3–4 problems on weak topic1.5 hr
SatVirtual contest or rated round2 hr
SunSpaced repetition review + light problems1 hr

Intermediate (1200–1800) & Advanced (1800+)

The structure stays the same — scale up intensity and duration. Intermediate: 10–15 hrs/week with problems at rating + 200, mixed topic sets (2 DP + 2 graphs + 1 math), and implementing one algorithm from scratch each Sunday. Advanced: 12–20 hrs/week with rating + 200 to + 400, writing mini-editorials for hardest solves, and re-deriving proofs of key theorems during review days.

Practice Session Timer (C++)

Here is a simple command-line utility to time your practice sessions, log them, and track cumulative hours per topic. Compile with g++ -std=c++17 -O2 and run it when you start a session.

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

struct Session {
    string date;
    string topic;
    int minutes;
    int problems_solved;
    int problems_attempted;
};

string today() {
    auto now = chrono::system_clock::now();
    time_t t = chrono::system_clock::to_time_t(now);
    char buf[11];
    strftime(buf, sizeof(buf), "%Y-%m-%d", localtime(&t));
    return string(buf);
}

void log_session(const Session& s, const string& logfile) {
    ofstream out(logfile, ios::app);
    out << s.date << ","
        << s.topic << ","
        << s.minutes << ","
        << s.problems_solved << ","
        << s.problems_attempted << "\n";
    out.close();
    cout << "Session logged to " << logfile << "\n";
}

void show_stats(const string& logfile) {
    ifstream in(logfile);
    if (!in.is_open()) {
        cout << "No log file found. Start practicing!\n";
        return;
    }

    map<string, int> topic_minutes;
    map<string, int> topic_solved;
    int total_min = 0, total_solved = 0, total_attempted = 0;
    int session_count = 0;
    string line;

    while (getline(in, line)) {
        stringstream ss(line);
        string date, topic, mins, solved, attempted;
        getline(ss, date, ',');
        getline(ss, topic, ',');
        getline(ss, mins, ',');
        getline(ss, solved, ',');
        getline(ss, attempted, ',');

        int m = stoi(mins), s = stoi(solved), a = stoi(attempted);
        topic_minutes[topic] += m;
        topic_solved[topic] += s;
        total_min += m;
        total_solved += s;
        total_attempted += a;
        session_count++;
    }
    in.close();

    cout << "\n===== Practice Statistics =====\n";
    cout << "Total sessions:  " << session_count << "\n";
    cout << "Total time:      " << total_min / 60 << "h "
         << total_min % 60 << "m\n";
    cout << "Total solved:    " << total_solved << " / "
         << total_attempted << " attempted\n";
    if (total_attempted > 0)
        cout << "Solve rate:      "
             << fixed << setprecision(1)
             << 100.0 * total_solved / total_attempted << "%\n";

    cout << "\n--- Hours per Topic ---\n";
    for (auto& [topic, mins] : topic_minutes) {
        double pct = 100.0 * mins / total_min;
        cout << setw(18) << left << topic
             << mins / 60 << "h " << mins % 60 << "m"
             << "  (" << fixed << setprecision(1) << pct << "%)\n";
    }
    cout << "===============================\n\n";
}

int main() {
    const string LOGFILE = "practice_log.csv";

    cout << "=== CP Practice Timer ===\n";
    cout << "1) Start timed session\n";
    cout << "2) View statistics\n";
    cout << "3) Exit\n";
    cout << "Choice: ";

    int choice;
    cin >> choice;

    if (choice == 2) {
        show_stats(LOGFILE);
        return 0;
    }
    if (choice != 1) return 0;

    cout << "Topic (dp/graphs/math/ds/greedy/strings/other): ";
    string topic;
    cin >> topic;

    cout << "\nSession started. Press Enter when done...\n";
    auto start = chrono::steady_clock::now();
    cin.ignore();
    cin.get();
    auto end = chrono::steady_clock::now();

    int elapsed = chrono::duration_cast<chrono::minutes>(end - start).count();
    if (elapsed < 1) elapsed = 1;

    cout << "Session duration: " << elapsed << " minutes\n";
    cout << "Problems solved: ";
    int solved; cin >> solved;
    cout << "Problems attempted: ";
    int attempted; cin >> attempted;

    Session s{today(), topic, elapsed, solved, attempted};
    log_session(s, LOGFILE);
    show_stats(LOGFILE);

    return 0;
}
Usage: Run the timer at the start of every practice session. Over weeks, the statistics view will reveal your true topic distribution, average session length, and overall solve rate — data that is far more useful than gut feelings.

Measuring Improvement

Rating is the most visible metric, but also the noisiest. A single bad contest can drop you 100 points. Track a dashboard of metrics that smooth out variance.

Key Metrics to Track

1. Solve Rate by Difficulty Band

For each 200-point difficulty band (e.g., 1200–1400, 1400–1600), track what percentage of problems you solve independently (no hints, no editorial). A healthy sign is your solve rate at rating + 200 climbing from 30% to 60% over 2–3 months.

2. Average Time per Problem

Track how long it takes you to AC problems at each difficulty level. Improvement shows up as decreasing time at a fixed difficulty. If your time at 1400-rated problems drops from 35 min to 20 min, that is genuine progress even if your rating has not moved.

3. Topic Coverage Heatmap

Visualise the number of problems solved per topic over the last 30 days. Gaps in coverage predict contest weaknesses. If you have not touched graph problems in 3 weeks, expect to struggle on the next graph problem in a contest.

4. Wrong Submission Rate

Track the ratio of WA/TLE/RE submissions to total submissions. A decreasing ratio means you are writing more correct code on the first try — a sign of better debugging habits and cleaner implementation skills.

5. Virtual Contest Performance

After each virtual contest, record your rank percentile compared to the original participants. Plot this over time. Are you consistently placing in the top 30% of your division? Top 20%? This is a cleaner signal than rating because it is not affected by rating inflation or specific contest difficulty.

Monthly Review Protocol

At the end of each month, spend 30 minutes reviewing your data:

  1. Topic balance: Are you hitting the target distribution from the rotation section? Which topic is lagging?
  2. Difficulty progression: Have you moved up a band on the difficulty ladder? If not, what is blocking you?
  3. Consistency: How many days did you practice? The single best predictor of improvement is practice frequency — not duration per session.
  4. Contest trend: Plot your last 10 rated contest results. Is the trend line going up, flat, or down?
  5. Action items: Based on the above, write 2–3 specific goals for next month. Example: "Solve 15 graph problems at 1600+ difficulty" or "Reduce WA rate from 30% to 20%."
The compound effect: A 1% daily improvement compounds to a 37x improvement over a year. You will not notice progress day to day — but if you compare your current self to 3 months ago, the gap should be obvious. Trust the process, track the data, and keep showing up.

The most important insight from tracking is this: what gets measured gets managed. The moment you start recording your practice, you will naturally gravitate toward the behaviours that improve the metrics — more deliberate difficulty selection, better topic balance, and more consistent daily practice.

Putting It All Together

Here is the complete practice system in one concise checklist:

  1. Target the right difficulty: Rating + 100 to + 300. Move up when solve rate hits 60–70%.
  2. Rotate topics: Use the 4-week cycle. Patch weaknesses first.
  3. Log everything: Problem, topic, difficulty, time, key insight, mistakes.
  4. Review with spaced repetition: 1, 3, 7, 14, 30, 90 day intervals.
  5. Do 2–3 virtual contests per week. Upsolve every missed problem.
  6. Measure monthly: Solve rate, time per problem, topic coverage, contest trend.
  7. Stay consistent: 5 focused days per week beats 2 marathon sessions.
Final thought: The contestants who reach 2000+ rating are not the ones who solved the most problems. They are the ones who extracted the most learning from every problem they solved. Be that kind of practitioner.