
| # | User | Rating |
|---|---|---|
| 1 | Benq | 3676 |
| 2 | Kevin114514 | 3659 |
| 3 | jiangly | 3640 |
| 4 | turmax | 3450 |
| 5 | tourist | 3382 |
| 6 | strapple | 3360 |
| 7 | Um_nik | 3354 |
| 8 | squareOf105 | 3348 |
| 9 | maroonrk | 3312 |
| 10 | idontwannadothis…e | 3191 |
| # | User | Contrib. |
|---|---|---|
| 1 | Qingyu | 155 |
| 2 | nik_exists | 151 |
| 3 | maspy | 149 |
| 4 | AmShZ | 145 |
| 5 | Um_nik | 143 |
| 6 | Errichto | 139 |
| 7 | BledDest | 136 |
| 8 | adamant | 134 |
| 9 | maroonrk | 132 |
| 10 | cry | 130 |
Hi Codeforces and problem setters!
While preparing a problem in Polygon, I once again ran into a classic headache when validating tests with incorrect/suboptimal solutions (e.g., greedy approaches, wrong heuristics, or $$$O(NK)$$$ DP instead of $$$O(N \log C)$$$).
Imagine a standard situation: a problem setter writes a flawed solution to make sure the test suite catches it.
Here is what happens in practice:
Question: Which tag should we assign to this solution in Polygon?

Right now, problem setters have to invent ugly crutches just to satisfy Polygon's strict tag system:
We spend time micro-optimizing intentionally wrong solutions just to appease the invocation checker, which feels counterproductive.

Add a WA or TLE tag (or a broader INCORRECT / ANY_ERROR tag) to Polygon.
A solution passes verification if it fails on the testset with either WA, TLE, ML, or RE, as long as it is rejected by the testsuite and does not get an overall OK.
This small addition would save problem setters a ton of time and make task development in Polygon much smoother.
What do you think?
UPDATE: Turns out I was just sleep-deprived. The Incorrect tag does exactly what I needed. Thanks for reading!

Is it just me, or have recent Div. 2 / Div. 3 contests become almost entirely constructive algorithms and ad-hoc math?
Look at the recent B, C, and D problems — virtually zero standard DP, graphs, or classic data structures. It’s usually just "stare at the statement for 20 minutes, spot the weird mathematical trick, write 5 lines of code". If you spot the trick instantly, you get a massive +delta. If not, you get stuck on Problem B for an hour, even if you know advanced CS inside out.
I get why problem setters are doing this. It stops people from mindlessly pasting Segment Tree templates, it lowers the barrier for beginners, and it makes life harder for ChatGPT/Claude cheaters.
But the variance on these ad-hoc tasks is insane. Sometimes it feels like CF is losing its algorithmic soul compared to platforms like AtCoder, which still feel much cleaner and more structure-oriented.
Not saying constructive problems are inherently bad, but the current balance feels a bit off. Would you guys prefer a return to more standard DS/DP in Div. 2/3, or do you actually enjoy this meta?
Honestly, China getting 4 golds every single IOI is just insane at this point. Half the time the fight for #1 individual spot is just between teammates!
People meme about secret routines, but looking at their actual setup, it totally makes sense:
Out of tens of thousands of participants, only 50 make the national team gathering (集训队), and then just 4 go to IOI. There are easily 20+ Chinese coders every year who didn't make the top 4 but would easily get a high gold if they represented almost any other country!
Top 50 in NOI means automatic admission to Tsinghua (like Yao Class) or PKU, completely skipping the Gaokao nightmare. That’s a life-changing reward, so kids start grinding full-time from middle school.
Places like Changjun or Hangzhou No. 2 are practically CP academies with full-time coaches, and platforms like Luogu make high-quality problems and editorials super accessible.
A few questions for the Chinese CPers here:
What does the daily routine actually look like when preparing for NOI / CTS? How many hours a day are spent solving problems vs theory?
Do you feel the 4-person country cap hides just how deep the talent pool actually is?
Would love to hear your thoughts or stories from the CTS grind!
Hello Codeforces.
Thank you to everyone who participated in Amateur Round 2! I hope you enjoyed the problemset and found the tasks interesting!
Below you will find the winners, tutorial and analysis for all the problems. If you have any questions, spot any errors, or have alternative approaches, feel free to discuss them in the comments!
The standings include only official Div. 3 participants, whose maximum peak rank was Specialist or lower when the contest started.
A special shoutout to the top — 9 participants who managed to solve all the problems — absolute legends!
Try to extract the individual digits of the 3-digit number and check the given conditions directly using a basic if statement.
Let the three-digit password be $$$s$$$. We can extract its digits $$$d_1$$$, $$$d_2$$$, and $$$d_3$$$ from left to right using integer division and modulo operations:
(Alternatively, you can read the input as a 3-character string $$$S$$$ and convert each character to a digit).
Once we have all three digits, we check if either condition is satisfied:
If at least one condition is true, output YES. Otherwise, output NO.
Complexity:
Time Complexity: $$$O(1)$$$ per testcase, since extracting digits and checking condition takes constant operations.
Space Complexity: $$$O(1)$$$, as we only need a few integer variables.
#include <iostream>
using namespace std;
int main() {
int s; cin >> s;
int d3 = s % 10;
int d2 = (s / 10) % 10;
int d1 = s / 100;
if ((d1 + d3 == d2) || (d2 == d3)) cout << "YES" << endl;
else cout << "NO" << endl;
}
Sorting the array helps process elements in order. Once sorted, what is the smallest valid value each coin can take to remain unique?
To minimize the number of operations, we can use a greedy approach.
Important Note:
In the worst case (e.g., all $$$N = 2 \cdot 10^5$$$ elements are initially equal), the total number of operations can reach roughly $$$\frac{N(N-1)}{2} \approx 2 \cdot 10^{10}$$$. This exceeds the 32-bit integer limit, so we must use 64-bit integers (long long in C++) for the total operations counter and array values.
Complexity:
Time Complexity: $$$O(N \log N)$$$ due to sorting the array. The linear scan takes $$$O(N)$$$ time.
Space Complexity: $$$O(N)$$$ to store the initial array.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
int n; cin >> n;
vector<long long> coins(n);
for (int i = 0; i < n; i++) cin >> coins[i];
sort(coins.begin(), coins.end());
long long ops = 0;
for (int i = 1; i < n; i++) {
if (coins[i] <= coins[i - 1]) {
long long target_year = coins[i - 1] + 1;
ops += (target_year - coins[i]);
coins[i] = target_year;
}
}
cout << ops << "\n";
}
Notice that if a length $$$L$$$ allows us to cut at least $$$K$$$ segments, any smaller length $$$L' \lt L$$$ will also yield at least $$$K$$$ segments. Can we use this monotonic property to binary search the maximum length $$$L$$$?
Let $$$f(L)$$$ be the maximum number of segments of length $$$L$$$ that Bob can cut from all $$$N$$$ ribbons:
Notice that $$$f(L)$$$ is a non-increasing function: as the requested length $$$L$$$ increases, the total number of segments $$$f(L)$$$ decreases or stays the same.
This monotonic property allows us to perform binary search on the answer:
If no length $$$L \ge 1$$$ satisfies the condition, the answer remains $$$0$$$.
Complexity:
Time Complexity: $$$O(N \log(\max A_i))$$$ — calculating $$$f(\text{mid})$$$ takes $$$O(N)$$$ time, and binary search takes $$$O(\log(\max A_i))$$$ iterations.
Space Complexity: $$$O(N)$$$ to store the ribbon lengths.
#include <iostream>
#include <vector>
using namespace std;
bool check(int n, int k, vector<long long>& ribbons, long long mid) {
long long count = 0;
for (long long& x : ribbons) {
count += (x / mid);
}
return count >= k;
}
int main() {
int n, k; cin >> n >> k;
vector<long long> ribbons(n);
for (int i = 0; i < n; i++) cin >> ribbons[i];
long long left = 1, right = 1000000000, L = 0;
while (left <= right) {
long long mid = right - (right - left) / 2;
if (check(n, k, ribbons, mid)) {
left = mid + 1;
L = mid;
}
else right = mid - 1;
}
cout << L << "\n";
}
Since $$$A_i \le 2 \cdot 10^5$$$, can we iterate over all possible target lengths $$$L$$$? How can we quickly calculate the max profit for a fixed $$$L$$$ without iterating through all $$$N$$$ ribbons individually?
For a chosen segment length $$$L$$$, let's analyze the net happiness gained from a single ribbon of length $$$A_i$$$:
Since Bob can choose which ribbons to cut, he will only cut a ribbon if its net profit is strictly positive ($$$P \gt 0$$$). Otherwise, he simply ignores it.
Notice that for a fixed length $$$L$$$, all ribbons with lengths falling in the same range $$$[k \cdot L + 1, (k + 1) \cdot L - 1]$$$ yield the exact same profit $$$P_{\text{remain}}$$$, and all ribbons with length exactly $$$k \cdot L$$$ yield $$$P_{\text{exact}}$$$.
This allows us to optimize the check for each $$$L$$$:
By iterating over all $$$L \in [1, M]$$$ and their multiples $$$j$$$, the total number of iterations bound by the Harmonic series:
Complexity:
Time Complexity: $$$O(N + M \log M)$$$, where $$$M = \max(A_i)$$$. Building frequency and prefix arrays takes $$$O(N + M)$$$ time, and iterating over all pairs $$$(L, j)$$$ takes $$$O(M \log M)$$$ time.
Space Complexity: $$$O(N + M)$$$ to store the array, frequency counts, and prefix sums.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
long long n, c; cin >> n >> c;
vector<long long> ribbons(n);
for (long long& x : ribbons) cin >> x;
long long maxA = *max_element(ribbons.begin(), ribbons.end());
vector<long long> cnt(maxA + 1, 0), pref(maxA + 1, 0);
for (long long x : ribbons) cnt[x]++;
for (int i = 1; i <= maxA; i++) pref[i] = pref[i - 1] + cnt[i];
long long cost = 0;
for (long long l = 1; l <= maxA; l++) {
long long selection = 0;
for (long long j = 1; l * j <= maxA; j++) {
long long P_exact = j * l - (j - 1) * c, P_remain = j * l - j * c;
if (P_exact > 0) selection += P_exact * cnt[j * l];
if (P_remain > 0 && min((j + 1) * l - 1, maxA) >= j * l + 1) selection += P_remain * (pref[min((j + 1) * l - 1, maxA)] - pref[j * l]);
}
cost = max(cost, selection);
}
cout << cost << "\n";
}
Notice that the total cost to save Bob by choosing hospital $$$i$$$ consists of two independent parts:
How much health does Bob actually need to recover?
First, let's determine the exact amount of health Bob needs:
Now we can break the problem down into two steps:
1. Shortest Paths (Dijkstra's Algorithm):
We need to find the minimum travel cost from starting city $$$0$$$ to all other cities. Since all edge weights $$$w \ge 1$$$ are non-negative, we can run Dijkstra's algorithm starting from node $$$0$$$. Let $$$\text{dist}[u]$$$ be the shortest distance from node $$$0$$$ to city $$$u$$$.
2. Evaluating Hospitals:
For each hospital $$$i$$$ located at city $$$id_i$$$:
We iterate through all $$$K$$$ hospitals, compute their total costs, and output the minimum total cost. If no hospital is reachable, we output -1.
Complexity:
#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;
const long long INF = 4e18;
struct Hospital {
int id;
long long hotel;
long long hosp;
long long h;
};
struct Edge {
int to;
long long weight;
};
int main() {
int n, m; cin >> n >> m;
long long x; cin >> x;
int k; cin >> k;
vector<Hospital> hospitals(k);
for (int i = 0; i < k; ++i) cin >> hospitals[i].id >> hospitals[i].hotel >> hospitals[i].hosp >> hospitals[i].h;
vector<vector<Edge>> adj(n + 1);
for (int i = 0; i < m; ++i) {
int u, v;
long long w;
cin >> u >> v >> w;
adj[u].push_back({v, w});
adj[v].push_back({u, w});
}
vector<long long> dist(n + 1, INF);
priority_queue<pair<long long, int>, vector<pair<long long, int>>, greater<pair<long long, int>>> pq;
dist[0] = 0;
pq.push({0, 0});
while (!pq.empty()) {
long long d = pq.top().first;
int u = pq.top().second;
pq.pop();
if (d > dist[u]) continue;
for (auto edge : adj[u]) {
if (dist[u] + edge.weight < dist[edge.to]) {
dist[edge.to] = dist[u] + edge.weight;
pq.push({dist[edge.to], edge.to});
}
}
}
long long ans = INF;
long long needed_health = 4LL * x;
for (auto b : hospitals) {
if (dist[b.id] == INF) continue;
long long days = (needed_health + b.h - 1) / b.h;
long long treatment_cost = days * (b.hosp + b.hotel);
long long total_cost = dist[b.id] + treatment_cost;
ans = min(total_cost, ans);
}
if (ans == INF) cout << -1 << "\n";
else cout << ans << "\n";
}
If the elevator stops at floor $$$i$$$ and then next stops at floor $$$j$$$ ($$$i \gt j$$$), $$$m = i - j - 1$$$ floors are skipped. Can you find a closed formula for the total sadness of all residents on those skipped floors? After that, try Dynamic Programming.
Let's first calculate the total sadness generated between two consecutive elevator stops at floor $$$i$$$ and floor $$$j$$$ ($$$i \gt j$$$):
Summing this up for all $$$d$$$ from $$$1$$$ to $$$m$$$:
With this formula, we can calculate the sadness between any two stops in $$$O(1)$$$ time.
Dynamic Programming Approach
Let $$$\text{dp}[i]$$$ be the minimum total cost (energy + sadness) for servicing all floors from $$$n$$$ down to $$$i$$$, assuming that the elevator must stop at floor $$$i$$$.
Base Case: $$$\text{dp}[n] = c_n$$$ (the elevator always stops at floor $$$n$$$).
Transitions: From floor $$$i$$$, we iterate over all possible next lower stops $$$j$$$ ($$$1 \le j \lt i$$$):
The final answer will be stored in $$$\text{dp}[1]$$$.
Complexity:
Time Complexity: $$$O(n^2)$$$ — there are $$$n$$$ DP states, and for each state $$$i$$$ we iterate over up to $$$n$$$ previous states $$$j$$$.
Space Complexity: $$$O(n)$$$ to store the DP array and costs array.
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main() {
int n, k; cin >> n >> k;
vector<long long> c(n);
for (long long& x : c) cin >> x;
vector<long long> dp(n + 1, 1e18);
dp[n] = c[n - 1];
for (int i = n; i >= 2; i--) {
if (dp[i] == 1e18) continue;
for (int j = i - 1; j >= 1; j--) {
if (i - j >= k || j == 1) {
long long m = i - j - 1;
long long sadness = m * (m + 1) * (m + 2) / 6;
dp[j] = min(dp[j], dp[i] + c[j - 1] + sadness);
}
}
}
cout << dp[1] << "\n";
}
Hi Codeforces.
Today I came across a legendary problem: 896E - Welcome home, Chtholly. It’s rated $$$3100$$$ and is known for being a nightmare for data structure enthusiasts. The tags say "DSU" and "Data Structures," and the intended solutions involve complex Sqrt-decomposition or Segment Tree Beats.
But as my future handle suggests -- $$$SegTreeIsAMyth$$$. I decided to see if a simple $$$O(N \cdot M)$$$ brute force could pass.
To my surprise, it didn't just pass: it $$$crushed$$$ the tests in $$$1750ms$$$ (with a 3s limit)!

The core of my solution is just a simple loop. No complex trees, no heavy pointers.
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define all(x) x.begin(), x.end()
#define rall(x) x.rbegin(), x.rend()
#define yes cout << "YES\n"
#define no cout << "NO\n"
#define debug cout << "Debug\n"
#pragma GCC optimize("O3")
#pragma GCC optimize("unroll-loops")
#pragma GCC target("avx2,bmi,bmi2,lzcnt,popcnt")
/*
---===ASCII help===---
'0' -> 48 '9' -> 57
'A' -> 65 'Z' -> 90
'a' -> 97 'z' -> 122
*/
signed main() {
ios_base::sync_with_stdio(false); cin.tie(nullptr);
int n, m; cin >> n >> m;
int arr[n];
for (int i = 0; i < n; ++i) cin >> arr[i];
while (m--) {
int t, l, r, x; cin >> t >> l >> r >> x;
--l; --r;
if (t == 1) {
for (int i = l; i <= r; ++i) {
if (arr[i] > x) arr[i] -= x;
}
}
else {
ll ans = 0;
for (int i = l; i <= r; ++i) {
if (arr[i] == x) ++ans;
}
cout << ans << '\n';
}
}
}
Many of you might think $$$10^{10}$$$ operations shouldn't pass in 3 seconds. Here is why it did:
GCC 14 & AVX2: The #pragma GCC target("avx2") allows the compiler to use SIMD instructions. It processes multiple integers in a single CPU cycle.
Loop Unrolling: #pragma GCC optimize("unroll-loops") reduces the overhead of the loop control.
Cache Locality: A simple array traversal is the most cache-friendly operation possible. While a Segment Tree jumps around in memory causing cache misses, the CPU prefetcher loves this linear scan.
Modern Judges: Codeforces' upgraded hardware is incredibly fast at executing vectorized code.
Next time you see a 3100-rated problem, don't be afraid. Sometimes, the "dumb" solution with the right pragmas is more powerful than the most sophisticated data structure.
Hello again. Thank you for participating. I hope that you liked the problems. Sorry for the mistakes in constraints and statement. In other rounds, i will check all tasks twice, to don't allow that mistakes. There is an Editorial:
This is just a implementation problem:
The idea was just check each car name is complete or not, and then get only unique one's count.
#include <bits/stdc++.h>
using namespace std;
/*
---===ASCII help===---
'0' -> 48 '9' -> 57
'A' -> 65 'Z' -> 90
'a' -> 97 'z' -> 122
*/
const long long mod = 1e9 + 7;
void solve() {
int n; cin >> n;
set<string> cars;
string s;
while (getline(cin, s)) {
vector<string> words;
stringstream str(s);
string word;
while (str >> word) words.push_back(word);
if (words.size() == 2 && words[0].size() >= 5 && words[1].size() >= 5) cars.insert(s);
}
cout << cars.size() << "\n";
}
int main() {
int t = 1;
//cin >> t;
while (t--) {
solve();
}
}
This is a brute-force problem because of constraints. You should do a brute-force from l to r and check for current number is Armstrong number or not.
#include <bits/stdc++.h>
using namespace std;
/*
---===ASCII help===---
'0' -> 48 '9' -> 57
'A' -> 65 'Z' -> 90
'a' -> 97 'z' -> 122
*/
const long long mod = 1e9 + 7;
bool check(int x) {
string str = to_string(x);
long long sum = 0;
for (char c : str) {
sum += pow(c - '0', str.size());
}
return sum == x;
}
void solve() {
int l, r; cin >> l >> r;
int ans = 0;
for (int i = l; i <= r; i++) {
if (check(i)) ans++;
}
cout << ans << "\n";
}
int main() {
//freopen("factory.in", "r", stdin);
//freopen("factory.out", "w", stdout);
int t = 1;
//cin >> t;
while (t--) {
solve();
}
}
This one is tricky, because you can't just brute-force. Instead, we can precompute! Because there are a small amount of Armstrong numbers, we can just find all of them by sacrificing our PC, and then get all Armstrong numbers, put it in array and done! Or, if you want more math variant, you can do this:
For a fixed digit lenght d:
n = c[0] * 0^d + c[1] * 1^d.... + c[9] * 9^d, Where c[i] = how many times digit i appears. Constraints:
c[0] + c[1]... + c[9] = d.
So instead of iterating over numbers, we iterate over digit frequency distributions.
This is a classical Stars and Bars problem:
Distribution count: 17! / 9! * 8! = 24310. This is extremely fast.
Precompute pow[d][digit] = digit^d. Then, for each digit lenght d = 1...8:
Generate all distributions:
c[0] + c[1]...c[9] = d
Then compute:
num = sum(c[i] + pow[d][i]), Add to array, then output the answer for l, r.
#include <bits/stdc++.h>
using namespace std;
/*
---===ASCII help===---
'0' -> 48 '9' -> 57
'A' -> 65 'Z' -> 90
'a' -> 97 'z' -> 122
*/
const long long mod = 1e9 + 7;
vector<long long> arm;
long long pw[10][10];
int cnt[10], cur[10];
void gen(int d, int pos, int left) {
if (pos == 10) {
if (left) return;
long long s = 0;
for (int i = 0; i < 10; i++) s += 1LL * cnt[i] * pw[d][i];
string t = to_string(s);
if ((int)t.size() != d) return;
memset(cur, 0, sizeof cur);
for (char c : t) cur[c - '0']++;
if (memcmp(cnt, cur, sizeof cnt) == 0) arm.push_back(s);
return;
}
for (int i = 0; i <= left; i++) {
cnt[pos] = i;
gen(d, pos + 1, left - i);
}
}
void solve() {
for (int d = 1; d <= 8; d++) for (int i = 0; i <= 9; i++) pw[d][i] = pow(i, d);
for (int d = 1; d <= 8; d++) {
memset(cnt, 0, sizeof cnt);
gen(d, 0, d);
}
sort(arm.begin(), arm.end());
arm.erase(unique(arm.begin(), arm.end()), arm.end());
long long l, r;
cin >> l >> r;
if (l > r) {
cout << 0;
return;
}
cout << upper_bound(arm.begin(), arm.end(), r) - lower_bound(arm.begin(), arm.end(), l);
}
int main() {
//freopen("speeding.in", "r", stdin);
//freopen("speeding.out", "w", stdout);
int t = 1;
//cin >> t;
while (t--) {
solve();
}
}
We should have a fast access to messages, their ID's, and who send that message. For that we can use std::map. We should declare:
map<int, set<int>> user; // The users messages(`std::set` for fast deleting and checking, we have any message here).
map<int, int> message; // The message ID, and who send.
ans = 0; // answer right now(for answer to third query fast)
m = 0; // The ID of the message, which we will give to the next message.
After that, the task already solved:
If we get first type query, just add that message to given person. If this message is the first for this person, increase answer to 1.
If we get second type query, just delete that message by ID. If the person don't have any messages, decrease answer by 1.
Else output the answer.
#include <bits/stdc++.h>
using namespace std;
/*
---===ASCII help===---
'0' -> 48 '9' -> 57
'A' -> 65 'Z' -> 90
'a' -> 97 'z' -> 122
*/
const long long mod = 1e9 + 7;
void solve() {
map<int, set<int>> user;
map<int, int> message;
int q, ans = 0, m = 1; cin >> q;
while (q--) {
int type; cin >> type;
if (type == 1) {
int x; cin >> x;
user[x].insert(m);
message[m] = x;
m++;
if (user[x].size() == 1) ans++;
}
else if (type == 2) {
int x; cin >> x;
user[message[x]].erase(x);
if (user[message[x]].size() == 0) ans--;
}
else cout << ans << "\n";
}
}
int main() {
int t = 1;
//cin >> t;
while (t--) {
solve();
}
}
Hello everyone. I invite you to the Amateur Round 1 (Div. 4), which already started in 07.01.2026 and ends in 15.01.2026.
You will be given 4 problems to solve. One problem will be divided into subtasks. The round will be in ICPC style.
The problems authored by zeyd1234.
I want to say "Thank you" to:
If you are using Python, use sys.stdin.readline() instead of input() to prevent EOFError.
To the Standing will only include true Div. 4 participants, who have maximum Pupil in their rank when contest started.
And congratulations to top — 15 by official standing:
And this is a top — 10 by only WA penalty count:
| Name |
|---|


