Why this matters when you build with AI
An AI assistant will write you a practice plan in seconds. The plan will look sensible: arrays on Monday, graphs on Wednesday, a mock contest on Sunday. The trouble is that the assistant does not know your results. It cannot see that you solved four graph problems last month and none of the dynamic programming ones, or that your last three rounds ended with no solve after the second hour. A plan written without that data is a guess about a person it has never met.
The habit this lesson teaches is to let your own results drive the plan. You keep a short log of every problem you try, with its topic and whether you solved it. You use that log to choose the next topics. You also decide in advance what you will do after a bad round, so you do not decide it while annoyed. An AI tool is useful for turning your log into a plan, or for checking a plan you wrote, as long as you give it the log and check its output against the log yourself.
A rating is a number that a judging site gives you after each contest. It goes up or down depending on how you did compared with the other people in that round. A rating band is a range of ratings grouped under one name or colour. Codeforces, for example, colours and names its bands. Band edges and names can change, so check the current rating page on the site you use before you rely on any number. This lesson uses bands only as a way to pick problem difficulty, not as a score you have to reach.
A topic is the kind of idea a problem needs, such as prefix sums, graphs, or dynamic programming. A weak topic is a topic where your solve rate is low over enough tries that the low rate is probably real, not bad luck. A solve rate is solved problems divided by attempted problems.
This lesson uses C++17 for the log analyser and Python for the brute force that checks it. We compiled with g++ 15.2.0 on Ubuntu and ran Python 3.14. Your timings will differ. The counts do not depend on the machine.
The idea: plan from data, not from a feeling
A practice plan answers three questions: what topics, what difficulty, and how you will know it worked.
Topics. Pick the topics where your solve rate is lowest among the topics you have tried often enough. A topic you have tried once tells you nothing. Three attempts is the smallest set that gives a rate worth reading, and more is better. If you have never attempted a topic, it is not yet a weak topic. It is an unknown, and you should put one or two problems from it in the plan so you learn something.
Difficulty. Pick problems that are a little above your current level. A problem that you can solve in one try with no new idea does not teach you much. A problem that needs an idea you have never used teaches a lot, but you will often fail it. The useful zone is a problem that you can solve after a few failed attempts, once you see the key idea. Take a problem you cannot solve in about 45 minutes and read the editorial or the hint section. Then solve a similar problem with no help. The rule is the same for every band: the problem should stretch you by one idea, not by three.
Checking. Write down, before you start, what success looks like. For example: "Solve two of four graph problems within 40 minutes each." Then compare the result with the plan the next week. A plan without a check is just a list of problems.
The topic log
A topic log is a plain text file with one line per problem you tried in practice or in a contest. Each line has the topic, the difficulty band you picked, and whether you solved it. You can keep it in a spreadsheet, but a text file is easier to process with a script. The format used in this lesson is:
topic solvedtopic is one word with no spaces. solved is 1 if you got an accepted verdict and 0 otherwise. A line like graphs 0 means you tried a graph problem and did not solve it.
Once you have the log, a short program can print your solve rate per topic. The next section shows one.
The bad round rule
A zero-solve round means a contest where you submitted nothing accepted. These rounds are common, even for strong people. The danger is not the round itself. The danger is what you do next: you may enter the next contest tired and worried, and try harder problems than you can handle, which gives another zero.
Decide the rule now, while you are calm. A simple rule that works:
- Do not enter another timed contest the same day.
- Within two days, upsolve one problem from that round. Upsolving means solving a problem you did not finish, after the contest, with the same time limit you would use in a contest. Read the hint section only after 45 minutes of trying.
- Add the round's problems to the topic log with
solvedset to0. - In the next practice session, do a problem one band below your usual choice, from a topic that is not your weakest. This rebuilds the feeling of solving something.
The rule is short on purpose. A long rule is one you will not follow after a bad round.
Worked example: a weak-topic report from a log
Here is an original task. Write it in your own words if you use it for practice.
You are given a topic log with
nlines (1 <= n <= 200000). Each line has a topic name of lowercase letters (at most 20 characters) and a numbersolvedthat is 0 or 1. Consider only topics that appear in at least 3 lines. For each such topic, print the topic, its totalsolvedcount, and its number of lines, astopic solved/attempts. Sort the output by solve rate from lowest to highest. If two topics have the same rate, sort them by name.
Example input:
8
arrays 1
arrays 0
arrays 1
graphs 0
graphs 0
graphs 0
dp 1
dp 1dp appears only twice, so it is left out. arrays has 2 solved out of 3, rate 0.667. graphs has 0 out of 3, rate 0. So graphs comes first.
Step 1: the test by hand
For this sample the answer is two lines: graphs 0/3 and arrays 2/3. Before you write code, check that you understand the filter. A topic with exactly 3 lines is included. A topic with 2 lines is excluded, even if both are solved.
Step 2: the standard technique
Count per topic with a map, then keep the topics with at least 3 lines, then sort. Compare two solve rates without division. For rates sx/ax and sy/ay, the first is smaller when sx * ay < sy * ax. Integer products avoid rounding errors, which matter when two rates are very close.
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
map<string, pair<long long, long long>> stats; // topic -> (attempts, solved)
for (int i = 0; i < n; i++) {
string topic;
int solved;
cin >> topic >> solved;
stats[topic].first++;
stats[topic].second += solved;
}
vector<tuple<string, long long, long long>> rows; // topic, attempts, solved
for (const auto& [topic, p] : stats) {
if (p.first >= 3) rows.emplace_back(topic, p.first, p.second);
}
// Weakest first: solved/attempts ascending, compared without division.
sort(rows.begin(), rows.end(), [](const auto& x, const auto& y) {
long long ax = get<1>(x), sx = get<2>(x);
long long ay = get<1>(y), sy = get<2>(y);
if (sx * ay != sy * ax) return sx * ay < sy * ax;
return get<0>(x) < get<0>(y);
});
for (const auto& [topic, attempts, solved] : rows) {
cout << topic << " " << solved << "/" << attempts << "\n";
}
return 0;
}We compiled it with g++ -std=c++17 -O2 -Wall -Wextra and got no warnings. On the sample it printed:
graphs 0/3
arrays 2/3Step 3: a brute force to check it
The brute force does the same job with Python's Fraction type, which stores exact fractions, and a plain loop. It is slower in principle but easy to read.
import sys
from fractions import Fraction
def main():
data = sys.stdin.read().split()
n = int(data[0])
attempts = {}
solved = {}
pos = 1
for _ in range(n):
topic = data[pos]
s = int(data[pos + 1])
pos += 2
attempts[topic] = attempts.get(topic, 0) + 1
solved[topic] = solved.get(topic, 0) + s
topics = [t for t in attempts if attempts[t] >= 3]
topics.sort(key=lambda t: (Fraction(solved[t], attempts[t]), t))
for t in topics:
print(f"{t} {solved[t]}/{attempts[t]}")
if __name__ == "__main__":
main()We wrote a generator that makes a random log of 1 to 30 lines over five topics, and we ran both programs on seeds 1 to 500. The C++ output matched the Python output on all 500 inputs. This tests small logs with many ties, which is where the sort rule matters most. It does not test the largest inputs, so for the real limit you should also time a log of 200000 lines.
Complexity
Reading the log takes O(n log t) with a map, where t is the number of distinct topics. Sorting the topics takes O(t log t). Both are fast for any realistic log. Memory is O(t).
Typical pitfalls
- Dividing as doubles and comparing equal rates. Two rates that should be equal, such as 1/3 and 2/6, can differ by a tiny amount after rounding. The integer comparison above avoids that.
- Counting the topic before the filter. Filter on attempts, not on solved count. A topic with 0 solved and 3 attempts is the most important one to see.
- Reading the topic as a number. A topic name read into an int stops at the first space or letter and corrupts the rest of the input.
Hands-on exercise: a four-week plan with a topic log
Do this on your own machine. You will need your last one to three months of practice and contest results. If you have no data yet, start the log today and use the first week to fill it.
- Make a file called
topic_log.txtin thetopic solvedformat. Put in at least 20 lines. Use real problems you tried. Do not make up results. - Compile the program from the worked example and run it on your file. Write the output in a notes file.
- Pick your two weakest topics that have at least three attempts. If you have fewer than two such topics, the weakest topic you have tried twice is the second one, and note that the data is thin.
- Write a four-week plan. Each week needs: one weak topic, three or four problems with a difficulty note, one practice contest or timed set, and a check that you can judge at the end of the week.
- Write your bad round rule in your own words, using the four steps above or a version you prefer. Keep it under ten lines.
- Optionally, give the log and your plan to an AI assistant with the prompt below. Check every number it mentions against your file.
Here is my topic log. Each line is a topic and whether I solved the problem (1) or not (0).
Using only these lines, list the topics that have at least three attempts, with solved/attempts and the rate.
Then suggest two weak topics. Do not invent problems or results.
Mark anything you are unsure about.
[paste the log here]A good answer lists only the topics in your file, gives the same counts you get from the program, and says when the data is too thin to judge. Check it by running the program. If the assistant gives a rate that does not match your count, trust the count and do not use the answer.
Your plan should pass these checks:
- Each week's weak topic comes from your log, not from a general list.
- The check at the end of each week is something you can count.
- The bad round rule has a step that tells you what to do the next day, not only a feeling to manage.
Show the answer
A correct output for the sample log in this lesson is:
graphs 0/3
arrays 2/3For your own plan, there is no single answer. A sign that the plan is wrong: it lists a weak topic that your log does not show, or it has no way to check the week.
Practice problems
Problem 1. Change the program so that it keeps topics with at least 4 attempts instead of 3. What does the sample print?
Hint: only the constant in the filter changes.
Show the answer
The sample has arrays with 3 attempts and graphs with 3 attempts. Both are dropped, so the output is empty. The program prints nothing.
Problem 2. Two topics have the same rate. Explain why the program sorts them by name, and what changes if you remove that tie break.
Hint: think about what a fixed order means when you compare two runs of the program.
Show the answer
Without the tie break, the order of two equal-rate topics depends on the order in which the map yields them, and a sort is not required to keep them in any particular order. With the tie break, the same log always gives the same output, which makes diffs between weeks easy to read.
Problem 3. Write a Python version of the bad round rule as a check. Input is a list of rounds, each with a count of solved problems. The output is the rounds with zero solved. Give the check on three example rounds.
Hint: a list comprehension over the rounds is enough.
Show the answer
Output: ['round A', 'round C']. The value is what you would copy into the topic log as solved 0 lines for each problem in those rounds.
Common mistakes
Planning from the list of topics, not from the log. A plan that says "dynamic programming week" without a count from the log is a plan about a textbook, not about you. Check it against the numbers.
Using a single round as the evidence. One contest with four topics gives one or two attempts per topic. Wait until a topic has three attempts before you call it weak.
Picking problems far above your level. When every problem is above your level, you learn to skip problems, not to solve them. Pick a few above your level and keep most at a level where you can try two or three ideas.
Changing the plan after every bad day. The bad round rule is there so you do not change the plan after one bad result. A plan should be judged over its whole four weeks.
Skipping the zero-solve round in the log. Rounds with no solve are the most useful rows in the log. If you leave them out, your rates look better than your results.
Checklist
- Log every practice and contest problem, even the ones you did not solve.
- Pick weak topics only from topics with at least three attempts.
- Keep a few problems just above your level, and most at a level you can solve with two or three ideas.
- Check each plan's week with a number you can count.
- After a zero-solve round: no timed contest the same day, upsolve one problem within two days, log every problem as
0. - Check every number an AI assistant gives you against your own log.
Summary
A good practice plan starts from your own log. Your weakest topics with enough attempts are the best place to train. You choose problems that stretch you by one idea, and you set a count-based check for each week. After a zero-solve round, a fixed rule stops one bad result from changing your training.